Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

compare_length_with #6

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

Conversation

cfcs
Copy link

@cfcs cfcs commented Jun 4, 2019

This PR provides a compare_length_with in the style of List.compare_length_with (hence the name).
It serves the same purpose as List.compare_length_with: It can be used to replace instances like

if 6 = M.cardinal t then

(which runs in O(n) time and may be very slow with a large t) with

if 0 = M.compare_length_with t 6 then

(which runs in O( min(cardinal t, 6+1) ) time).

Two examples of where this can be useful, but I suspect this is a fairly common use-case in code:

@hannesm
Copy link
Owner

hannesm commented Jun 12, 2019

I'm not at all a fan of that API in the Lists module (neither of adding more functions and mutable state to Gmap). The first call to cardinal checks an invariant for tsig-keys (which contain a single dnskey, thus cardinal is rather fast); the second call is a workaround as commented in the todo, and should be fixed properly.

are there any other cardinal uses you have found that would benefit from compare_length_with?

@cfcs
Copy link
Author

cfcs commented Jun 17, 2019

Why do you dislike the API in the List module?

Here's another example, similarly guarded with a "TODO" comment about LRU, but probably a better example of where this would shine:
https://github.com/roburio/udns/blob/4493b1ca50c749d3dbeb013f0a2d2bf6fb9b2fbc/server/dns_server.ml#L676-L677

I couldn't find other examples in udns, and not sure what other users of gmap there are.

I did a quick grep through my locally checked out code and found some uses of Map.cardinal where this might make sense in an attempt to backup my argument that partial comparison makes sense to have for map:

Most of the other instances I saw was either

  • code that could/should be replaced with is_empty t, not (is_empty t)
  • code that compared cardinal t = 1, which makes me wonder if an is_singleton function would be sufficient

So basically no conclusive evidence that this is needed.

It also seems like it is counter-intuitive to a lot of people that cardinal is O(n) in the stdlib, so maybe that is an indicator that it should be added to the documentation of Map.Make, but again that is irrelevant to this PR.

Anyway, here's a take on it without mutable state, but it's a lot more verbose than the mutable state version:

let compare_with_length t num =
  let rec loop seen = function
  | Seq.Nil -> seen - num
  | Seq.Cons _ when seen > num -> 1
  | Seq.Cons ( _ , next) -> loop (succ seen) (next ())
  in loop 0 (M.to_seq t ())

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants