Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Define Map.update.#1026

Closed
sbriais wants to merge4 commits intoocaml:trunkfromsbriais:map_update
Closed

Define Map.update.#1026

sbriais wants to merge4 commits intoocaml:trunkfromsbriais:map_update

Conversation

sbriais
Copy link
Contributor

@sbriaissbriais commentedFeb 1, 2017
edited by alainfrisch
Loading

The following pattern seems frequent:

try Map.add key map (f (Map.find key map)) with Not_found -> Map.add key map default

It can be implemented more efficiently when having access to the concrete representation of maps to avoid to traverse twice the tree. This PR implements one possible solution to this problem and illustrates this by replacing two such patterns.

EDIT: Cfhttps://caml.inria.fr/mantis/view.php?id=7309 -- AF

kit-ty-kate reacted with thumbs up emoji
@@ -86,6 +86,16 @@ module type S =
of [x] in [m] disappears.
@before 4.03 Physical equality was not ensured. *)

val update: key -> ('a option -> 'a) -> 'a t -> 'a t
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Do you have a rationale for the signature off ? My instinct would rather say'a option -> 'a option.

Copy link
ContributorAuthor

@sbriaissbriaisFeb 2, 2017
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I can see two semantics for f returning None:

  1. do not add/update the binding (we are happy with the value already bound or the absence of binding)
  2. remove the binding

I am unsure of what is the most natural choice. What do you have in mind with your proposal?
My guess is that the current interface has a more obvious behaviour.

Copy link
Contributor

@dbuenzlidbuenzliFeb 2, 2017
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

  1. note that 1. is easy to perform byid onSome _ orNone

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

It would good to ensure that if we return the same option (physically, or perhaps checking physical equality under theSome), the function returns the original map (physically) and doesn't allocate anything.

Copy link
Member

@gaschegascheFeb 2, 2017
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This is, in fact, already done by@sbriais's implementation. But it could also be useful to specify it in the interface documentation.

(The current implementation uses'a option -> 'a, which corresponds indeed to checking physical equality under theSome -- this is strictly better than checking physical equality of the option, as the option will not be kept anyway.)

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

The documentation already says that (same phrasing as add documentation).
The current implementation is really copied-pasted from add, but f is called to compute data, instead of taking it as an argument.

@@ -86,6 +86,16 @@ module type S =
of [x] in [m] disappears.
@before 4.03 Physical equality was not ensured. *)

val update: key -> ('a option -> 'a) -> 'a t -> 'a t
(** [update x f m] returns a map containing the same bindings as
[m], plus a binding of [x] to [f z] where [z] is either [None]
Copy link
Contributor

@dbuenzlidbuenzliFeb 1, 2017
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

this could be shortened to where [z] is [find_opt x m].

@gasche
Copy link
Member

In Batteries we haveupdate, which does something different, andmodify which does what is currently proposed.

update is the Map equivalent ofSet.S.update : elt -> elt -> t -> t, which is used to replace an element for another element that is equivalent for the given order (but may differ in irrelevant fields).Map.S.update is the lifted version of this, with the typekey -> key -> 'a -> 'a t -> 'a t, and is specified by the fact thatupdate k k' v m is only valid whenk andk' have the same ordering, and is then equivalent toadd k' v (remove k m).

modify comes in three variants:

valmodify:key -> ('a ->'a) ->'at ->'at(** [modify k f m] replaces the previous binding for [k] with [f] applied to      that value. If [k] is unbound in [m] or [Not_found] is raised during the      search, [Not_found] is raised.*)valmodify_def:'a ->key -> ('a ->'a) ->'at ->'at(** [modify_def v0 k f m] replaces the previous binding for [k]    with [f] applied to that value. If [k] is unbound in [m] or    [Not_found] is raised during the search, [f v0] is    inserted (as if the value found were [v0]).*)valmodify_opt:key -> ('aoption ->'aoption) ->'at ->'at(** [modify_opt k f m] allows to modify the binding for [k] in [m]      or absence thereof.*)

The Base library has two functions for value modification, the'a option -> 'a version and the'a option -> 'a option version.

val change  :  ('k, 'v, 'cmp) t  -> 'k  -> f:('v option -> 'v option)  -> ('k, 'v, 'cmp) tval update  :  ('k, 'v, 'cmp) t  -> 'k  -> f:('v option -> 'v)-> ('k, 'v, 'cmp) t

Finally, Containers only has the'a option -> 'a option version

  val update : key -> ('a option -> 'a option) -> 'a t -> 'a t

I think that if we are to add only one, the'a option -> 'a option version probably makes the most sense. It is a bit less efficient as it requires allocation ofSome blocks.

Should the name beupdate,change,modify or something else?

Finally, I think that changes to the standard library should come with unit tests. If that can help, here are themodify_opt tests in Batteries:

  let test_modify_opt () =    let sum t = M.fold (+) t 0 in    let t = il [(1, 2); (3, 4)] in    (* usage to modify values *)    let test1 k t =      "sum (modify_opt k (+1 or 2) t) = sum t + (mem k t ? 1 : 2)" @?        (sum (M.modify_opt k (function None -> Some 2 | Some x -> Some (x+1)) t) =         sum t + if M.mem k t then 1 else 2) in    test1 1 t;    test1 57 t;    (* usage to delete values *)    "modify_opt k (fun _ -> None) t -> remove k" @?      (M.modify_opt 3 (function Some _ -> None | None -> None) t |> M.mem 3 |> not);    "modify_opt k (fun _ -> None) (singleton k) -> empty" @?      (M.singleton 1 0 |> M.modify_opt 1 (fun _ -> None) |> M.is_empty);    (* usage to add values *)    "modify_opt k (fun None -> Some x) t -> add k" @?      (M.modify_opt 2 (function None -> Some 1 | _ -> assert false) t |> M.mem 2);    ()

@dbuenzli
Copy link
Contributor

I think that if we are to add only one, the 'a option -> 'a option version probably makes the most sense.

I would be in favor of adding this one and this oneonly.

OvermindDL1 reacted with thumbs up emoji

@sbriais
Copy link
ContributorAuthor

Regarding the tests, what is the policy? Add a new file, or extend the existing test for maps (ie basic/maps.ml) ?

@gasche
Copy link
Member

I don't know of a policy, that would be for the stdlib expert (@alainfrisch ) to decide. I'm fine with whichever, I thinkbasic/maps.ml is just fine.

(The one part of the policy that I feel strongly about is that each new addition to the stdlib should come with tests. It is even easier for me to say as the implementor of a buggy new function in 4.04.0 thatshould have been better tested.)

@alainfrisch
Copy link
Contributor

The stdlib expert is fine with whichever as well, but this is more a question for the testsuite expert.

@dra27
Copy link
Member

@Sbrais Tests definitely OK in the existing file. The only reason to branch off to a separate file would be if the test is particularly long (or, obviously, requires an extra harness of its own).

@sbriaissbriaisforce-pushed themap_update branch 3 times, most recently froma8305b0 toddba465CompareFebruary 15, 2017 09:12
@alainfrisch
Copy link
Contributor

Everything looks good to me. Will merge if nobody objects to it shortly.

@sbriais : can you add a reference to#7309 to the Change file?

@sbriais
Copy link
ContributorAuthor

Done. Should I fill also the "..." for the reviewers with all the names of the participants in this thread?

Changes Outdated
@@ -83,6 +83,10 @@ Next version (4.05.0):

### Standard library:

- PR#7309, GPR#1026: Add update to maps. Allows to update a binding in
a map or create a new binding if the key had no binding. (Sébastien

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Please respect the format and break the line before the open paren.

@damiendoligez
Copy link
Member

Should I fill also the "..." for the reviewers with all the names of the participants in this thread?

Only if you feel they have meaningfully contributed. For example, don't add me.

@alainfrisch
Copy link
Contributor

Rebased and merged on trunk (25105b2). Thanks@sbriais!

stedolan pushed a commit to stedolan/ocaml that referenced this pull requestMar 21, 2023
EmileTrotignon pushed a commit to EmileTrotignon/ocaml that referenced this pull requestJan 12, 2024
* Refactoring: List.skip -> List.drop* Remove useless parameter* Remove useless local definition* Less code* Formatting---------Co-authored-by: Cuihtlauac ALVARADO <cuihtmlauac@tarides.com>
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@damiendoligezdamiendoligezdamiendoligez left review comments

@gaschegaschegasche left review comments

@dbuenzlidbuenzlidbuenzli left review comments

@alainfrischalainfrischalainfrisch left review comments

Assignees
No one assigned
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

6 participants
@sbriais@gasche@dbuenzli@alainfrisch@dra27@damiendoligez

[8]ページ先頭

©2009-2025 Movatter.jp