hacks

(tidier examples of) random scripts from throughout the years
Log | Files | Refs | README

ropes.sml (2894B)


      1 (* Editor's note: transmuting an assignment from one CMU class to another...
      2  * Sometimes things are easier when the language isn't fighting you :)
      3  * Comments are lightly edited. Written around the spring of 2024. *)
      4 
      5 datatype rope = Leaf of string | Cat of rope ref * rope ref
      6 
      7 structure Rope = 
      8 struct
      9 fun size (ref (Leaf s)) = String.size s
     10   | size (ref (Cat(l, r))) = size l + size r
     11 
     12 fun charat (ref (Leaf s)) i = String.sub (s, i)
     13   | charat (ref (Cat(l, r))) i = let 
     14       val leftSize = size l
     15   in
     16     if leftSize > i then charat l i else charat r (i - leftSize)
     17   end
     18     
     19 fun toString (ref (Leaf s)) = s
     20   | toString (ref (Cat(l, r))) = (toString l) ^ (toString r)
     21 
     22 fun join (L, R) = ref (Cat(L, R))
     23 
     24 (* Nasty casework: the use of refcells and my lack of knowledge of their semantics also
     25  * means I had to punt the casework to a case expr so that I could return the original
     26  * reference if needed. *)					  
     27 fun sub R lo hi = 
     28     if (lo = 0 andalso hi = (size R)) then R else 
     29     (case R of 
     30          (ref (Leaf s)) => ref (Leaf(String.substring(s, lo, hi))) 
     31        | (ref (Cat(l, r))) => let 
     32              val leftSize = size l 
     33          in
     34              (case (hi < size l, size l <= lo) of
     35 				  (* is the substring all on the left? then recur on left *)
     36                   (true, _) => sub l lo hi
     37                   (* vice versa *)								   
     38                 | (_, true) => sub r 0 (hi - size l)
     39 				  (* otherwise take what we want from either side *)
     40                 | _ => join (sub l lo leftSize, sub r 0 (hi-leftSize))) 
     41          end)
     42 
     43 (* Takes a list of ropes and modifies them to reuse subtrees from earlier elements in
     44  * the list (thus 'reducing' the overall size of the collection of ropes). *)
     45 fun reduce ropes = let
     46     (* Gross and imperative code both in the hashtable usage and in the overuse of let
     47      * blocks to do things in sequence. In my defense it was 2AM when I had this idea. *)
     48     val ropeEqual = fn (R1, R2) => (toString R1) = (toString R2)
     49     val ropeHash = fn R => HashString.hashString (toString R)
     50     exception NotFound
     51     val H = HashTable.mkTable (ropeHash, ropeEqual) (100, NotFound)
     52                               
     53     fun reduceHelp H R  = 
     54         HashTable.lookup H R 
     55         handle NotFound => 
     56                let val () = (HashTable.insert H (R,R)) in 
     57                    (case R of (ref (Leaf s)) => R 
     58                             | (ref (Cat (l, r))) => join (reduceHelp H l, reduceHelp H r))
     59                end
     60     in
     61         map (fn x => let val () = x := !(reduceHelp H x) in x end) ropes        
     62 end
     63 end
     64 
     65 val r1 = ref (Cat (ref (Leaf "a"), ref (Leaf "br")))
     66 val r2 = ref (Cat (ref (Leaf "abr"), ref (Leaf "a")))
     67 val r3 = ref (Cat (ref (Leaf "a"), ref (Cat (ref (Leaf "br"), ref (Leaf "a")))))
     68 
     69 (* seems to work and like... it typechecks™ 
     70  * (but seriously, not rigorously tested) *)
     71 val out = Rope.reduce [r1, r2, r3]