From 2c0098326270295ccd8a47130841be497abffb5a Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 3 Nov 2016 22:06:41 +0100 Subject: [PATCH] split `CCList.Zipper` into its own module, `CCZipper` in containers.data --- _oasis | 2 +- src/core/CCList.ml | 82 -------------------------------------- src/core/CCList.mli | 93 ------------------------------------------- src/data/CCZipper.ml | 82 ++++++++++++++++++++++++++++++++++++++ src/data/CCZipper.mli | 87 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 170 insertions(+), 176 deletions(-) create mode 100644 src/data/CCZipper.ml create mode 100644 src/data/CCZipper.mli diff --git a/_oasis b/_oasis index efe0ea9b..a4f5cd90 100644 --- a/_oasis +++ b/_oasis @@ -60,7 +60,7 @@ Library "containers_data" CCMixmap, CCRingBuffer, CCIntMap, CCPersistentArray, CCMixset, CCHashconsedSet, CCGraph, CCHashSet, CCBitField, CCHashTrie, CCBloom, CCWBTree, CCRAL, CCAllocCache, - CCImmutArray, CCHet + CCImmutArray, CCHet, CCZipper BuildDepends: bytes # BuildDepends: bytes, bisect_ppx FindlibParent: containers diff --git a/src/core/CCList.ml b/src/core/CCList.ml index e02de8e2..65b55f72 100644 --- a/src/core/CCList.ml +++ b/src/core/CCList.ml @@ -975,88 +975,6 @@ module Assoc = struct *) end -(** {2 Zipper} *) - -module Zipper = struct - type 'a t = 'a list * 'a list - - let empty = [], [] - - let is_empty = function - | [], [] -> true - | _ -> false - - let to_list (l,r) = List.rev_append l r - - let to_rev_list (l,r) = List.rev_append r l - - (*$Q - Q.(pair (list small_int)(list small_int)) (fun z -> \ - Zipper.to_list z = List.rev (Zipper.to_rev_list z)) - *) - - let make l = [], l - - let left = function - | x::l, r -> l, x::r - | [], r -> [], r - - let left_exn = function - | x::l, r -> l, x::r - | [], _ -> invalid_arg "zipper.left_exn" - - let right = function - | l, x::r -> x::l, r - | l, [] -> l, [] - - let right_exn = function - | l, x::r -> x::l, r - | _, [] -> invalid_arg "zipper.right_exn" - - let modify f z = match z with - | l, [] -> - begin match f None with - | None -> z - | Some x -> l, [x] - end - | l, x::r -> - begin match f (Some x) with - | None -> l,r - | Some _ -> l, x::r - end - - let is_focused = function - | _, [] -> true - | _ -> false - - let focused = function - | _, x::_ -> Some x - | _, [] -> None - - let focused_exn = function - | _, x::_ -> x - | _, [] -> raise Not_found - - let insert x (l,r) = l, x::r - - let remove (l,r) = match r with - | [] -> l, [] - | _ :: r' -> l, r' - - (*$Q - Q.(triple int (list small_int)(list small_int)) (fun (x,l,r) -> \ - Zipper.insert x (l,r) |> Zipper.remove = (l,r)) - *) - - let drop_before (_, r) = [], r - - let drop_after (l, r) = match r with - | [] -> l, [] - | x :: _ -> l, [x] - - let drop_after_and_focused (l, _) = l, [] -end - (** {2 References on Lists} *) module Ref = struct diff --git a/src/core/CCList.mli b/src/core/CCList.mli index 2ed996c5..b6f01ecc 100644 --- a/src/core/CCList.mli +++ b/src/core/CCList.mli @@ -341,99 +341,6 @@ module Assoc : sig @since 0.17 *) end -(** {2 Zipper} *) - -module Zipper : sig - type 'a t = 'a list * 'a list - (** The pair [l, r] represents the list [List.rev_append l r], but - with the focus on [r]. *) - - val empty : 'a t - (** Empty zipper *) - - val is_empty : _ t -> bool - (** Empty zipper? Returns true iff the two lists are empty. *) - - (*$T - Zipper.(is_empty empty) - not ([42] |> Zipper.make |> Zipper.right |> Zipper.is_empty) - *) - - val to_list : 'a t -> 'a list - (** Convert the zipper back to a list. - [to_list (l,r)] is [List.rev_append l r] *) - - val to_rev_list : 'a t -> 'a list - (** Convert the zipper back to a {i reversed} list. - In other words, [to_list (l,r)] is [List.rev_append r l] - @since 0.14 *) - - val make : 'a list -> 'a t - (** Create a zipper pointing at the first element of the list *) - - val left : 'a t -> 'a t - (** Go to the left, or do nothing if the zipper is already at leftmost pos *) - - val left_exn : 'a t -> 'a t - (** Go to the left, or - @raise Invalid_argument if the zipper is already at leftmost pos - @since 0.14 *) - - val right : 'a t -> 'a t - (** Go to the right, or do nothing if the zipper is already at rightmost pos *) - - val right_exn : 'a t -> 'a t - (** Go to the right, or - @raise Invalid_argument if the zipper is already at rightmost pos - @since 0.14 *) - - val modify : ('a option -> 'a option) -> 'a t -> 'a t - (** Modify the current element, if any, by returning a new element, or - returning [None] if the element is to be deleted *) - - val insert : 'a -> 'a t -> 'a t - (** Insert an element at the current position. If an element was focused, - [insert x l] adds [x] just before it, and focuses on [x] - @since 0.14 *) - - val remove : 'a t -> 'a t - (** [remove l] removes the current element, if any. - @since 0.14 *) - - val is_focused : _ t -> bool - (** Is the zipper focused on some element? That is, will {!focused} - return a [Some v]? - @since 0.14 *) - - val focused : 'a t -> 'a option - (** Returns the focused element, if any. [focused zip = Some _] iff - [empty zip = false] *) - - val focused_exn : 'a t -> 'a - (** Returns the focused element, or - @raise Not_found if the zipper is at an end *) - - val drop_before : 'a t -> 'a t - (** Drop every element on the "left" (calling {!left} then will do nothing). - @since 0.14 *) - - val drop_after : 'a t -> 'a t - (** Drop every element on the "right" (calling {!right} then will do nothing), - keeping the focused element, if any. - @since 0.14 *) - - val drop_after_and_focused : 'a t -> 'a t - (** Drop every element on the "right" (calling {!right} then will do nothing), - {i including} the focused element if it is present. - @since 0.14 *) - - (*$= - ([1], [2]) (Zipper.drop_after ([1], [2;3])) - ([1], []) (Zipper.drop_after ([1], [])) - ([1], []) (Zipper.drop_after_and_focused ([1], [2;3])) - *) -end - (** {2 References on Lists} @since 0.3.3 *) diff --git a/src/data/CCZipper.ml b/src/data/CCZipper.ml new file mode 100644 index 00000000..44f666bd --- /dev/null +++ b/src/data/CCZipper.ml @@ -0,0 +1,82 @@ + +(* This file is free software, part of containers. See file "license" for more details. *) + +(** {1 List Zipper} *) + +type 'a t = 'a list * 'a list + +let empty = [], [] + +let is_empty = function + | [], [] -> true + | _ -> false + +let to_list (l,r) = List.rev_append l r + +let to_rev_list (l,r) = List.rev_append r l + +(*$Q + Q.(pair (list small_int)(list small_int)) (fun z -> \ + to_list z = List.rev (to_rev_list z)) +*) + +let make l = [], l + +let left = function + | x::l, r -> l, x::r + | [], r -> [], r + +let left_exn = function + | x::l, r -> l, x::r + | [], _ -> invalid_arg "zipper.left_exn" + +let right = function + | l, x::r -> x::l, r + | l, [] -> l, [] + +let right_exn = function + | l, x::r -> x::l, r + | _, [] -> invalid_arg "zipper.right_exn" + +let modify f z = match z with + | l, [] -> + begin match f None with + | None -> z + | Some x -> l, [x] + end + | l, x::r -> + begin match f (Some x) with + | None -> l,r + | Some _ -> l, x::r + end + +let is_focused = function + | _, [] -> true + | _ -> false + +let focused = function + | _, x::_ -> Some x + | _, [] -> None + +let focused_exn = function + | _, x::_ -> x + | _, [] -> raise Not_found + +let insert x (l,r) = l, x::r + +let remove (l,r) = match r with + | [] -> l, [] + | _ :: r' -> l, r' + +(*$Q + Q.(triple int (list small_int)(list small_int)) (fun (x,l,r) -> \ + insert x (l,r) |> remove = (l,r)) +*) + +let drop_before (_, r) = [], r + +let drop_after (l, r) = match r with + | [] -> l, [] + | x :: _ -> l, [x] + +let drop_after_and_focused (l, _) = l, [] diff --git a/src/data/CCZipper.mli b/src/data/CCZipper.mli new file mode 100644 index 00000000..0ccc2acb --- /dev/null +++ b/src/data/CCZipper.mli @@ -0,0 +1,87 @@ + +(* This file is free software, part of containers. See file "license" for more details. *) + +(** {1 List Zipper} + + @since 1.0 *) + +type 'a t = 'a list * 'a list +(** The pair [l, r] represents the list [List.rev_append l r], but + with the focus on [r]. *) + +val empty : 'a t +(** Empty zipper *) + +val is_empty : _ t -> bool +(** Empty zipper? Returns true iff the two lists are empty. *) + +(*$T + (is_empty empty) + not ([42] |> make |> right |> is_empty) +*) + +val to_list : 'a t -> 'a list +(** Convert the zipper back to a list. + [to_list (l,r)] is [List.rev_append l r] *) + +val to_rev_list : 'a t -> 'a list +(** Convert the zipper back to a {i reversed} list. + In other words, [to_list (l,r)] is [List.rev_append r l] *) + +val make : 'a list -> 'a t +(** Create a zipper pointing at the first element of the list *) + +val left : 'a t -> 'a t +(** Go to the left, or do nothing if the zipper is already at leftmost pos *) + +val left_exn : 'a t -> 'a t +(** Go to the left, or + @raise Invalid_argument if the zipper is already at leftmost pos *) + +val right : 'a t -> 'a t +(** Go to the right, or do nothing if the zipper is already at rightmost pos *) + +val right_exn : 'a t -> 'a t +(** Go to the right, or + @raise Invalid_argument if the zipper is already at rightmost pos *) + +val modify : ('a option -> 'a option) -> 'a t -> 'a t +(** Modify the current element, if any, by returning a new element, or + returning [None] if the element is to be deleted *) + +val insert : 'a -> 'a t -> 'a t +(** Insert an element at the current position. If an element was focused, + [insert x l] adds [x] just before it, and focuses on [x] *) + +val remove : 'a t -> 'a t +(** [remove l] removes the current element, if any. *) + +val is_focused : _ t -> bool +(** Is the zipper focused on some element? That is, will {!focused} + return a [Some v]? *) + +val focused : 'a t -> 'a option +(** Returns the focused element, if any. [focused zip = Some _] iff + [empty zip = false] *) + +val focused_exn : 'a t -> 'a +(** Returns the focused element, or + @raise Not_found if the zipper is at an end *) + +val drop_before : 'a t -> 'a t +(** Drop every element on the "left" (calling {!left} then will do nothing). *) + +val drop_after : 'a t -> 'a t +(** Drop every element on the "right" (calling {!right} then will do nothing), + keeping the focused element, if any. *) + +val drop_after_and_focused : 'a t -> 'a t +(** Drop every element on the "right" (calling {!right} then will do nothing), + {i including} the focused element if it is present. *) + +(*$= + ([1], [2]) (drop_after ([1], [2;3])) + ([1], []) (drop_after ([1], [])) + ([1], []) (drop_after_and_focused ([1], [2;3])) +*) +