Scheme 字典操作

Scheme dict operations

我想了解 lisp 中的一些 hashmap 代码,并使用 python 作为参考。

下面两个是不是大致一样?如果是这样,我怎样才能看到我的字典对象是什么样子的?

# create dict
>>> my_dict = dict()                
> (define my_dict (make-hash-table))

# add keys
>>> my_dict['a'] = 1
>>> my_dict['b'] = 2
> (hash-set! my_dict 'a 1)
> (hash-set! my_dict 'b 2)

# get values
>>> my_dict.get('a')
> (hash-ref my_dict 'a)

# print the values?
print (my_dict)
> ??? How can I see what my hashmap contains in scheme?

Are the following two roughly the same?

当然可以,但是我会通过参考 Python.

来学习 Scheme 或任何 Lisp。

请注意,R6RS Scheme has hash tables 和散列 table 过程是标准库的一部分; R5RS 方案并非如此。因此,早期的 Scheme 实现通常有自己的 hash table 实现。 Guile Scheme 确实支持 R6RS 哈希 tables 和过程,但似乎也保持对旧过程的支持。我最熟悉的Scheme Chez Scheme也是如此。 Chez Scheme 用户指南 说包含旧式过程主要是为了与旧的 Chez Scheme 实现兼容,但在未来的版本中可能会放弃对它们的支持。我不知道 Guile 对此的立场是什么,但我建议尽可能使用标准 Scheme 过程以获得最大的可移植性并防止未来在您的实现中发生此类变化。

撰写本文时,Guile 已达到 v3.0.2; OP 版本是 v2.0.9,据我所知目前已有 5 年历史。 Guile 2.0.14 supports R6RS hashtables,所以我怀疑 Guile 2.0.9 也是如此。不管怎样,我都会用R6RS Standard Scheme来回答;如有必要,适应旧的 Guile 实施应该是微不足道的。

R6RS Standard Library没有make-hash-table,而是有make-hashtablemake-eq-hashtablemake-eqv-hashtablemake-hashtable 过程需要一个测试键等价性的过程,而 make-eq-hashtable 测试与 eq? 的等价性,make-eqv-hashtable 测试与 eqv?.

而不是 hash-set!,标准库有 hashtable-set!,它采用与 OP 对 hash-set!.

相同的参数

而不是 hash-ref,标准库有 hashtable-ref。这与 OP hash-ref 的不同之处在于它需要第三个参数来指定找不到键时的默认 return 值。

跟随 OP 的脚步,可以创建和查询哈希 table:

Chez Scheme Version 9.5
Copyright 1984-2017 Cisco Systems, Inc.

> (define my-dict (make-eq-hashtable))
> (hashtable-set! my-dict 'a 1)
> (hashtable-set! my-dict 'b 2)
> (hashtable-set! my-dict 'c 3)
> (hashtable-set! my-dict 'd 5)
> (hashtable-set! my-dict 'e 7)
> (hashtable-set! my-dict 'f 11)
> (hashtable-set! my-dict 'g 13)
> (hashtable-ref my-dict 'e #f)
7
> (hashtable-ref my-dict 'h #f)
#f
> my-dict
#<eq hashtable>

How can I see what my hashmap contains in scheme?

可能最简单的做法是从散列 table 创建一个关联列表。 R6RS 中没有执行此操作的标准程序,但是 一个旧的 SRFI (SRFI-69) that included exactly such a procedure: hash-table->alist。Guile 的 OP 实现可能包含这样一个程序,但它是使用 R6RS 标准方案易于实施:

(define (hashtable->alist ht)
  (let-values (((ks vs) (hashtable-entries ht)))
    (vector->list (vector-map cons ks vs))))

这里,hashtable-entries 过程采用散列 table 和 return 的两个值:键向量和对应值向量; let-values 用于绑定两个 return 值,以便它们可以使用。 vector-map return 是将 ksvs 的元素与 cons 的元素组合而成的向量,vector->list 从该向量创建一个列表。

> (hashtable->alist my-dict)
((f . 11) (e . 7) (c . 3) (b . 2) (a . 1) (g . 13) (d . 5))

诡计:为了完整性

上面的解决方案在Guile中是可行的,但是很多R6RS程序在Guile中默认不被识别。要使上述解决方案在 Guile 中工作,首先需要如下内容:

(import (rnrs hashtables (6))         ; for R6RS hash tables
        (rnrs base (6)))              ; for R6RS let-values

在查看 Guile 2.0.14 参考手册 后,另一个非 portable 解决方案显而易见。手册中没有描述 hash-table->alist 过程,但是 the documentation describes 如何使用 hash-map->list 过程从散列 table 创建关联列表:

(hash-map->list cons my-dict)

这只能用于使用 Guile 的特定实现哈希 table 构造函数(例如 make-hash-table)创建的哈希 table,并且可能不适用于哈希 tables 使用 R6RS 构造函数创建(例如 make-eq-hashtable)。再次注意这个解决方案应该在 Guile 中工作(没有任何导入),但不是 portable.