320a19b9a117 — Chris Cannam 2 years ago
Add extractPrefix
6 files changed, 87 insertions(+), 1 deletions(-)

M persistent-array.sig
M test.sml
M trie-map-fn.sml
M trie-map-keyadapter-fn.sml
M trie-map.sig
M trie.sig
M persistent-array.sig +1 -1
@@ 24,7 24,7 @@ signature PERSISTENT_ARRAY = sig
     val all : ('a -> bool) -> 'a array -> bool
     val collate : ('a * 'a -> order) -> 'a array * 'a array -> order
 
-    (* II. Functions similar to ARRAY, but altered for persistent
+    (* II. Functions similar to ARRAY, but altered for a persistent
            array. The functions passed to modify/modifyi should return
            NONE for "no change" or SOME x to change the value. *)
 

          
M test.sml +43 -0
@@ 121,6 121,49 @@ functor TrieTestFn (ARG : TRIE_TEST_FN_A
                        T.isEmpty e1 andalso T.isEmpty e2
                    end
         ),
+        ( "extractPrefix-empty",
+          fn () => check_lists id (T.enumerate
+                                       (T.extractPrefix (T.empty, "parp")), [])
+        ),
+        ( "extractPrefix-matches",
+          fn () => check_lists id (T.enumerate
+                                       (T.extractPrefix (test_trie (), "pa")),
+			           [ "par", "parp" ])
+	           andalso
+	           check_lists id (T.enumerate
+                                       (T.extractPrefix (test_trie (), "par")),
+			           [ "par", "parp" ])
+	           andalso
+	           check_lists id (T.enumerate
+                                       (T.extractPrefix (test_trie (), "alligat")),
+			           [ "alligator" ])
+        ),
+        ( "extractPrefix-no-matches",
+          fn () => check_lists id (T.enumerate
+                                       (T.extractPrefix (test_trie (), "quiz")),
+                                   [ ])
+                   andalso
+                   check_lists id (T.enumerate
+                                       (T.extractPrefix (test_trie (), "aaa")),
+                                   [ ])
+                   andalso
+                   check_lists id (T.enumerate
+                                       (T.extractPrefix (test_trie (), "zzz")),
+                                   [ ])
+                   andalso
+                   check_lists id (T.enumerate
+                                       (T.extractPrefix (test_trie (), "parpy")),
+                                   [ ])
+                   andalso
+                   check_lists id (T.enumerate
+                                       (T.extractPrefix (test_trie (), "alligators")),
+                                   [ ])
+        ),
+        ( "extractPrefix-all-matches",
+          fn () => check_lists id (T.enumerate
+                                       (T.extractPrefix (test_trie (), "")),
+                                   sorted strings)
+        ),
         ( "enumeratePrefix-empty",
           fn () => check_lists id (T.enumeratePrefix (T.empty, "parp"), [])
         ),

          
M trie-map-fn.sml +28 -0
@@ 495,6 495,34 @@ functor TrieMapFn (A : TRIE_MAP_FN_ARG)
     fun enumeratePrefix (trie, e) =
         foldriPrefix (fn (k, v, acc) => (k, v) :: acc) [] (trie, e)
 
+    fun extractPrefixNode (xx, n) =
+        if K.isEmpty xx
+        then SOME n
+        else
+            case n of
+                LEAF item => NONE
+              | TWIG (kk, item) =>
+                (if isPrefixOf (K.explode xx, K.explode kk)
+                 then SOME n
+                 else NONE)
+              | BRANCH (_, m) =>
+                case M.find (m, K.head xx) of
+                    NONE => NONE
+                  | SOME nsub =>
+                    case extractPrefixNode (K.tail xx, nsub) of
+                        NONE => NONE
+                      | SOME nsub' => 
+                        SOME (BRANCH (NONE, M.modify (M.new (), K.head xx,
+                                                      fn _ => SOME nsub')))
+
+    fun extractPrefix (trie, e) =
+        case trie of
+            EMPTY => EMPTY
+          | POPULATED n =>
+            case extractPrefixNode (e, n) of
+                NONE => EMPTY
+              | SOME n => POPULATED n
+
     fun foldiNodeRange right f (rpfx, n, leftConstraintK, rightConstraintK, acc) =
         let fun f' (pfx, item, acc) =
                 f (K.implode pfx, item, acc)

          
M trie-map-keyadapter-fn.sml +3 -0
@@ 66,6 66,9 @@ functor TrieMapKeyAdapterFn (A : TRIE_MA
         T.foldriPrefix (fn (k, x, acc) => f (dekey k, x, acc))
                        acc (t, enkey k)
 
+    fun extractPrefix (t, k) =
+        T.extractPrefix (t, enkey k)
+                       
     fun enumeratePrefix (t, k) =
         map (fn (k, x) => (dekey k, x)) (T.enumeratePrefix (t, enkey k))
 

          
M trie-map.sig +6 -0
@@ 95,6 95,12 @@ signature TRIE_MAP = sig
         itself does not need to be present as a key in the trie *)
     val foldriPrefix : (key * 'a * 'b -> 'b) -> 'b -> ('a trie * key) -> 'b
 
+    (** Return a trie containing all key-value pairs in the trie that
+        have the given key as a prefix, sharing the structure of the
+        given trie as far as possible. The prefix itself does not need
+        to be present as a key in the trie *)
+    val extractPrefix : 'a trie * key -> 'a trie
+
     (** Return a list of all key-value pairs in the trie that have the
         given key as a prefix, in sort order by key. The prefix itself
         does not need to be present as a key in the trie *)

          
M trie.sig +6 -0
@@ 61,6 61,12 @@ signature TRIE = sig
         need to be present as an entry in the trie *)
     val foldrPrefix : (entry * 'a -> 'a) -> 'a -> (trie * entry) -> 'a 
 
+    (** Return a trie containing all entries in the trie that have the
+        given entry as a prefix, sharing the structure of the given
+        trie as far as possible. The prefix itself does not need to be
+        present as an entry in the trie *)
+    val extractPrefix : trie * entry -> trie
+
     (** Return a list of all entries in the trie that have the given
         entry as a prefix, in sort order. The prefix itself does not
         need to be present as an entry in the trie *)