Stdlib: New functions Map.find_after and Map.find_before#665
Stdlib: New functions Map.find_after and Map.find_before#665gerdstolpmann wants to merge 4 commits intoocaml:trunkfrom
Conversation
|
My proposal was a bit different, namely: val find_first: (key -> bool) -> 'a t -> (key * 'a) option
val find_last: (key -> bool) -> 'a t -> (key * 'a) optionwhere the predicate is assumed to be monotonic. One could also add: val partition: (key -> bool) -> 'a t -> 'a t * 'a t(And probably the same for |
|
This |
|
Yep, it's a little bit generalized. @c-cube You can reduce |
|
I don't see how an iterator-based approach would work. The idea here is that the lookup should take |
Except that because of the assumption on the predicate to be monotonic, the lookup is |
|
Sometimes it is good to have lunch. I finally got your idea, Alain, it's a bisection following the tree, right? Given that c-cube was also on the wrong track, I guess we should also name it like that, i.e. Implementation-wise there is a nasty case - when you get to a leaf for I wonder whether this is better, I'm not sure whether you always have a monotone f. This depends on the application. |
|
@alainfrisch I don't understand your remark about complexity. From what I understand of the code and what the documentation says, the given predicate will be called on every key greater than the given start (in the case of |
|
My comments applied to my proposal, not to this specific PR. To fix the idea, my proposal is: let rec find_first f = function
| Empty ->
None
| Node(l, v, d, r, _) ->
if f v then
match find_first f l with
| None -> Some (v, d)
| Some _ as r -> r
else
find_first f rIt's indeed a bisection, although bisecting seems more like an internal implementation detail. The specification is really: find the first element matching a monotonic predicate.
Typical uses would be: find_first (fun x -> compare x0 x <= 0)or find_first (fun x -> compare x0 x < 0)which correspond to the initial need (find the first binding after a given key, with the two strict / non-strict variants of "after"). |
|
@alainfrisch I like your proposal. Do you compared the speed of your version and a tail-rec one? |
|
A tail-rec version that avoids intermediate allocations: let rec find_first_aux v0 d0 f = function
| Empty ->
Some (v0, d0)
| Node(l, v, d, r, _) ->
if f v then
find_first_aux v d f l
else
find_first_aux v0 d0 f r
let rec find_first f = function
| Empty ->
None
| Node(l, v, d, r, _) ->
if f v then
find_first_aux v d f l
else
find_first f rI did not benchmark any of those, but at least the tailrec version seems hard to beat (except by providing versions specialized to given predicate forms). |
|
@alainfrisch Oh nice, I though only about the tail-rec version with allocation. Yours is better and indeed no benchmark is needed. If one day the optimizer can transform the tail-rec with allocation into a tail-rec without (the |
|
@gerdstolpmann Would my proposal cover your use cases? If so, feel free to include the code in your PR. I'd also suggest to add similar functions to |
I would not bet on that, and generally speaking, such hand-optimization in low-level common data structures are not too bad (more explicit performance profile, less dependent on compiler internals, benefits to alternative backends -- js_of_ocaml or Bucklescript). |
|
Thinking twice, it is probably less work to copy map.ml to my own sources and add it there than to push for a patch. Sorry for opening this, I should have known better. |
|
Just for fun, BuckleScript generates pretty good code for Alain's utility: https://bloomberg.github.io/bucklescript/js-demo/?gist=6f82819e4ec0dbf4ba4e625e1d9b67eb |
Finds a binding where the key is the ceiling for a given key. Addresses ocaml#665.
|
I've implemented a similar |
Finds the first/last binding where the key satisfies a monotonic predicate. Addresses ocaml#665, supersedes ocaml#868. Thanks @alainfrisch for the idea and most of the implementation.
|
And I've implemented something relatively complete (first and last, maps and sets) in #869, based on the above proposal. |
Finds the first/last binding where the key satisfies a monotonic predicate. Addresses ocaml#665, supersedes ocaml#868. Thanks @alainfrisch for the idea and most of the implementation.
|
You might want to look at the somewhat more general "closest_key" function in Core/Base: Having the ability to use > or >= as the criterion is valuable in my experience. |
|
The functions proposed in #869 do allow to use |
|
Ah, you're right. Sorry for the noise. On Tue, Oct 25, 2016 at 7:46 AM Alain Frisch [email protected]
|
Finds the first/last binding where the key satisfies a monotonic predicate. Addresses ocaml#665, supersedes ocaml#868. Thanks @alainfrisch for the idea and most of the implementation.
Finds the first/last binding where the key satisfies a monotonic predicate. Addresses ocaml#665, supersedes ocaml#868. Thanks @alainfrisch for the idea and most of the implementation.
Finds the first/last binding where the key satisfies a monotonic predicate. Addresses ocaml#665, supersedes ocaml#868. Thanks @alainfrisch for the idea and most of the implementation.
Finds the first/last binding where the key satisfies a monotonic predicate. Addresses ocaml#665, supersedes ocaml#868. Thanks @alainfrisch for the idea and most of the implementation.
Finds the first/last binding where the key satisfies a monotonic predicate. Addresses ocaml#665, supersedes ocaml#868. Thanks @alainfrisch for the idea and most of the implementation.
Finds the first/last binding where the key satisfies a monotonic predicate. Addresses #665, supersedes #868. Thanks @alainfrisch for the idea and most of the implementation.
Finds the first/last binding where the key satisfies a monotonic predicate. Addresses ocaml#665, supersedes ocaml#868. Thanks @alainfrisch for the idea and most of the implementation.
Simplify "make build_upstream"
This implements the suggestion of http://caml.inria.fr/mantis/view.php?id=7277
I'm going here with Alain's idea of iterating over the successors/predecessors of the key until the callback function returns true.