Zulip Chat Archive

Stream: lean4

Topic: Modify value of HashMap


Tomas Skrivan (Apr 05 2023 at 21:47):

How can I easily modify a value of a HashMap and ensure linearity of the value?

Is this doing what I want?

unsafe def modify {α β} [BEq α] [Hashable α] (m : HashMap α β) (key : α) (f : β  β) : HashMap α β :=
  if let .some val := m.find? key then
    let m := m.insert key (unsafeCast ()) -- ensures linearity?
    m.insert key (f val)
  else
    m

However this is effectively doing three look-ups in the hash table which is a waste. Is there a better way?

Eric Wieser (Apr 05 2023 at 22:46):

(docs4#Std.HashMap)

Tomas Skrivan (Apr 05 2023 at 23:12):

I don't think any of those functions do what I'm asking for. Maybe you can put it together with functions from HashMap.Impl but I do not really understand the inner workings of the implementation.

Mario Carneiro (Apr 05 2023 at 23:48):

It's not currently supported. Here's a quick implementation, the real thing requires doing some proofs:

import Std.Data.HashMap

/--
`O(n)`. Replace the first entry `a', b` in the list
with key equal to `a` to have key `a` and value `f a' b`.
-/
@[simp] def Std.AssocList.modify [BEq α] (a : α) (f : α  β  β) : AssocList α β  AssocList α β
  | nil         => nil
  | cons k v es => match k == a with
    | true  => cons a (f k v) es
    | false => cons k v (modify a f es)

namespace Std.HashMap

/-- Performs an in-place edit of the value, ensuring that the value is used linearly. -/
def Imp.modify [BEq α] [Hashable α] (m : Imp α β) (a : α) (f : α  β  β) : Imp α β :=
  let size, buckets := m
  let i, h := mkIdx buckets.2 (hash a |>.toUSize)
  let bkt := buckets.1[i]
  let buckets := buckets.update i .nil h -- for linearity
  size, buckets.update i (bkt.modify a f) ((Bucket.update_size ..).symm  h)⟩

variable [BEq α] [Hashable α]

/-- Performs an in-place edit of the value, ensuring that the value is used linearly. -/
unsafe def modifyImpl (self : HashMap α β) (a : α) (f : α  β  β) : HashMap α β :=
  self.1.modify a f, lcProof

/-- Performs an in-place edit of the value, ensuring that the value is used linearly. -/
@[implemented_by modifyImpl]
opaque modify (self : HashMap α β) (a : α) (f : α  β  β) : HashMap α β

end Std.HashMap

Eric Wieser (Apr 06 2023 at 00:20):

Would α → Option β → Option β be a more sensible signature? That way you can handle removing or inserting during the update too (without computing the hash twice)

Mario Carneiro (Apr 06 2023 at 00:21):

that's alter, it is a lot more complicated to prove

Tomas Skrivan (Apr 06 2023 at 00:47):

Nice, thanks! I'm happy to use an unproven version now. Having alter would be also very useful.

Mario Carneiro (Apr 06 2023 at 00:58):

done in https://github.com/leanprover/std4/commit/ed1f8fe


Last updated: Dec 20 2023 at 11:08 UTC