如何在Racket中映射哈希表的值

8

我想对哈希表中的值进行函数映射,像这样:

(hash-map add1 (hash "apple" 1 "pear" 2))
=> #hash(("apple" . 2) ("pear" . 3))

有没有一个库函数可以做到这一点?最好能够适用于不可变哈希表。
我在PlaneT上搜索,但没有找到任何东西。
现在,如果真的不存在,我会去写它。将它添加到标准库(和文档!)并提交请求,我应该在github上fork并添加它吗?或者我应该先把它放在PlaneT上,然后要求将其移动?我想帮忙,但是我不知道正确的方法是什么。
5个回答

8

有一个哈希表,但它返回一个列表[1]

如果要完全按照你的需求进行操作,你需要编写自己的hash-map

#lang racket

(define (new-hash-map f h)
  (make-immutable-hash (hash-map h (λ (k v) (cons k (f v))))))

(new-hash-map add1 (hash "apple" 1 "pear" 2))

; => '#hash(("pear" . 3) ("apple" . 2))

你可能感兴趣的另一种表单是 for/hash [2]:

#lang racket

(define (new-hash-map2 f h)
  (for/hash ([(k v) (in-hash h)]) (values k (f v))))

(new-hash-map2 add1 (hash "apple" 1 "pear" 2))

; => '#hash(("pear" . 3) ("apple" . 2))

如果您认为此功能应该包含在Racket中,则非常欢迎提交补丁!最好的贡献方式是在Github上分叉并提交拉取请求。

来自IRC频道的stamourv也推荐了for/hash,这就是我最终使用的工具。非常感谢。 - Theo Belaire
@TheoBelaire 推荐使用了什么样的推理?目前我没有看到任何一种变化的优势。 - Zelphir Kaltstahl
那是很久以前的事了,我记不清了。 我想它是因为不需要构建一个列表来构建哈希表,但这可能是错误的。 - Theo Belaire

2
在 Racket 中,您可以使用“hash-map”高阶过程(hash-map),它通常返回一个列表,其中包含接收的过程应用后生成的值,但它可以适应于就地修改映射。例如:
(define h (make-hash))
(hash-set! h "apple" 1)
(hash-set! h "pear" 2)

h
=> '#hash(("pear" . 2) ("apple" . 1))

技巧在于传递一个具有适当功能的lambda函数:
(hash-map h (lambda (key value)
              (let ((newval (add1 value)))
                (hash-set! h key newval)
                newval)))

h
=> '#hash(("pear" . 3) ("apple" . 2))

如果您需要更通用的解决方案,请尝试以下实现:

(define (mutable-hash-map! hash proc)
  (hash-map hash (lambda (key value)
                   (hash-set! hash key (proc value))))
  (void))

2
您可以使用in-hash-values迭代哈希表的值,并使用常规的for循环进行映射。函数in-hash-values获取哈希表并返回值序列,然后可以用for循环遍历。
示例:
(for/list ([elt (in-hash-values (hash "apple" 1 "pear" 2))])
  (add1 elt))

同样地,你可以使用sequence-map,但是你得到的是另一个序列而不是列表:
(sequence-map add1 (in-hash-values (hash "apple" 1 "pear" 2)))

2

由于哈希映射表在此处创建了不需要的列表,我宁愿使用哈希遍历

(define (hash-update-all! h func)
  (hash-for-each
    h
    (lambda (k v) (hash-set! h k (func v)))))

(define table (make-hash '((a . 1) (b . 2) (c . 3))))

(hash-update-all! table (curry * 100))

table

 ==> '#hash((c . 300) (a . 100) (b . 200))

编辑:我忘记了 for 可以处理哈希表:

(define (hash-update-all! h func)
  (for ([(k v) h])   (hash-set! h k (func v))))

(hash-update-all! table (curryr / 10))

table

 ==> '#hash((c . 30) (a . 10) (b . 20))

0
很多年之后,Racket 8.6添加了hash-map/copy来实现这一点。它将现有哈希表的键和值传递给一个映射函数,该函数返回两个值 - 用于新返回的表中的新键和值。
> (hash-map/copy (hash "apple" 1 "pear" 2) (lambda (k v) (values k (+ v 1))))
'#hash(("apple" . 2) ("pear" . 3))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接