Ben Kovitz
Ben Kovitz

Reputation: 5020

How do you match hash-table against the value of a local variable?

> (define h #hash((a . 11) (b . 0)))
> (define (f h key)
    (match h
      [(hash-table (key value)) value]
      [_ 'do-something-else]))
> (f h 'a)
'do-something-else  ;; expect 11

How should I modify the match pattern so the function above returns the same as (hash-ref h key 'do-something-else)?

One problem seems to be that match is auto-quoting key, so that it literally matches 'key rather than the value of the key local variable. A second problem is that match hash-table seems to want to match the entire hash table rather than just one key-value pair.

Upvotes: 2

Views: 561

Answers (1)

Alex Knauth
Alex Knauth

Reputation: 8373

How to duplicate the behavior of hash-ref

To duplicate the behavior of hash-ref, you need to do two things:

(1) Use a key pattern that only matches things that are equal to the value of the argument key. You can do this with the == pattern:

(hash-table ((== key) value))

(2) Match hash tables where there are other entries other than key. You can do this by adding _ ... to match the other entries.

(hash-table ((== key) value) _ ...)

In context:

(define (f h key)
  (match h
    [(hash-table ((== key) value) _ ...) value]
    [_ 'do-something-else]))

Makes (f h key) behave like (hash-ref h key 'do-something-else).

Why your first attempt didn't work

Your pattern:

(hash-table (key value))

Does not autoquote the key so that it literally matches 'key. Instead it matches any hash table that has only one entry:

> (match (hash 'anything-goes 11)
    [(hash-table (key value)) value]
    [_ 'do-something-else])
11

That's because key is interpreted as a pattern, and an identifier as a pattern matches anything. You can see this more clearly if you name it to something else and use it in the body:

> (define (f h key-arg)
    (match h
      [(hash-table (key-pat value))
       (printf "key-arg = ~v\n" key-arg)
       (printf "key-pat = ~v\n" key-pat)
       value]))
> (f (hash 'anything-goes 11) 'a)
key-arg = 'a
key-pat = 'anything-goes
11

When you write an identifier pattern that "happens" to have the same name as a local variable, it shadows it, just like this example:

> (let ([x "local variable"])
    (match (list 1 2 3)
      [(list 1 2 x)
       x]))
 3

The x in the pattern matches anything, so it's matched against the 3. The pattern shadows the x, so when the body uses x, it's referring to the 3, and not the local variable. The local variable was never actually used because of this shadowing.

Upvotes: 2

Related Questions