Skip to content

Commit

Permalink
feat(Trie): map_tag (#132)
Browse files Browse the repository at this point in the history
  • Loading branch information
favonia authored Jul 5, 2024
1 parent 662daed commit 2c48886
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 2 deletions.
11 changes: 11 additions & 0 deletions src/Trie.ml
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,17 @@ let of_seq s = of_seq_with_merger ~prefix:Emp (fun _ _ y -> y) s

type 'data untagged = ('data, unit) t

let rec map_tag_node f n =
{ tag_root = Option.map f n.tag_root
; tag_default_child = Option.map f n.tag_default_child
; tag_children = SegMap.map (map_tag_node f) n.tag_children
}

let[@inline] map_tag f : _ t -> _ t =
function
| None -> None
| Some (d, t) -> Some (d, map_tag_node f t)

let[@inline] retag t : _ t -> _ t =
function
| None -> None
Expand Down
7 changes: 5 additions & 2 deletions src/Trie.mli
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,13 @@ val of_seq_with_merger : ?prefix:bwd_path -> (bwd_path -> 'data * 'tag -> 'data
(** Untagged tries (where all tags are [()]). *)
type 'data untagged = ('data, unit) t

(** [retag tag t] changes all tags within [t] to [tag] in O(1) time. The data remain intact. *)
(** [map_tag f trie] applies [f] to all tags within [trie], which is equivalent to {!val:map}[ (fun _ (d, t) -> (d, f t)) trie] but can often be more efficient. The data remain intact. Note that if [f] is a constant function, use {!val:retag} for even more efficiency. *)
val map_tag : ('tag1 -> 'tag2) -> ('data, 'tag1) t -> ('data, 'tag2) t

(** [retag tag t] changes all tags within [t] to [tag] in O(1) time. It is equivalent to {!val:map_tag}[ (fun _ -> tag) t] but usually more efficient. The data remain intact. *)
val retag : 'tag -> ('data, _) t -> ('data, 'tag) t

(** [retag_subtree tag path t] changes all tags within the subtrie rooted at [path] to [tag] efficiently. The data remain intact. *)
(** [retag_subtree path tag t] changes all tags within the subtrie rooted at [path] to [tag] efficiently. The data remain intact. *)
val retag_subtree : path -> 'tag -> ('data, 'tag) t -> ('data, 'tag) t

(** [untag t] is [retag () t]. *)
Expand Down
1 change: 1 addition & 0 deletions test/ListAsTrie.ml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ let of_seq s = Seq.fold_left (union_singleton ~prefix:Emp (fun _ _ y -> y)) empt
let of_seq_with_merger ?(prefix=Emp) m s = Seq.fold_left (union_singleton ~prefix m) empty s

type 'a untagged = (path * ('a * unit)) list
let map_tag f l = List.map (fun (p, (d, t)) -> p, (d, f t)) l
let retag t l = List.map (fun (p, (d, _)) -> p, (d, t)) l
let retag_subtree pre t l =
List.map (fun ((p, (d, _)) as b) -> if Option.is_some (split_path pre p) then p, (d, t) else b) l
Expand Down
6 changes: 6 additions & 0 deletions test/TestTrie.ml
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,11 @@ let test_of_seq_with_merger =
to_list (Trie.of_seq_with_merger ?prefix f (List.to_seq l))
=
ListAsTrie.of_seq_with_merger ?prefix f (List.to_seq l))
let test_map_tag =
Q.Test.make ~count ~name:"map_tag"
Q.Gen.(pair (Q.fun1 Q.Observable.int int) gen_list)
~print:Q.Print.(pair Q.Fn.print print_list)
(fun (Fun (_, f), l) -> to_list (Trie.map_tag f (of_list l)) = ListAsTrie.map_tag f l)
let test_retag =
Q.Test.make ~count ~name:"retag" Q.Gen.(pair int gen_list) ~print:Q.Print.(pair int print_list)
(fun (t, l) -> to_list (Trie.retag t (of_list l)) = ListAsTrie.retag t l)
Expand Down Expand Up @@ -275,6 +280,7 @@ let () =
; test_to_seq_values
; test_of_seq
; test_of_seq_with_merger
; test_map_tag
; test_retag
; test_retag_subtree
; test_untag
Expand Down

0 comments on commit 2c48886

Please sign in to comment.