Scheme 中的数据结构

Data Structures in Scheme

我正在学习 Scheme,来自 Haskell 的背景,我 运行 遇到了一个非常令人惊讶的问题 - scheme 似乎没有自定义数据类型??? (即对象、结构等)。我知道有些实现有自己的自定义宏来实现结构,但 R6RS 本身似乎没有提供任何此类功能。

鉴于此,我有两个问题:

  1. 这是正确的吗?我是否缺少允许创建自定义数据类型的功能?
  2. 如果不是,方案程序员如何构建程序?

例如,任何试图 return 多项数据的函数都需要某种封装数据的方法。使用哈希映射是最佳做法吗?

(define (read-user-input)
    (display "1. Add todo\n2. Delete todo\n3. Modify todo\n")
    (let ((cmd-num (read)))
    (if (equal? cmd-num "1") '(("command-number" . cmd-num) ("todo-text" . (read-todo)))
    (if (equal? cmd-num "2") '(("command-number" . cmd-num) ("todo-id"   . (read-todo-id)))
                             '(("command-number" . cmd-num) ("todo-id"   . (read-todo-id)))))))
  1. 绝对不是。 Scheme 有几个用于自定义类型的 SRFI,又名。记录类型,对于 R7RS 红色版,它将是 SRFI-136, but since you mention R6RS it has records defined in the standard too.

使用 R6RS 的示例:

#!r6rs
(import (rnrs))

(define-record-type (point make-point point?)
  (fields (immutable x point-x)
          (immutable y point-y)))

(define test (make-point 3 7))
(point-x test) ; ==> 3
(point-y test) ; ==> 7
  1. 早期 Scheme(和 lisp)没有记录类型,您通常会创建构造函数和访问器:

示例:

(define (make-point x y)
  ...)

(define (point-x p)
  ...)

(define (point-y p)
  ...)

这与记录类型实际创建的合同相同。它是如何实现的并不重要。以下是一些想法:

(define make-point cons)
(define point-x car)
(define point-y cdr)

这在大多数情况下都有效,但并不是很安全。也许这样更好:

(define tag (list 'point))
(define idx-tag 0)
(define idx-x 1)
(define idx-y 2)

(define (point? p)
  (and (vector? p)
       (positive? (vector-length p))
       (eq? tag (vector-ref p idx-tag))))

(define (make-point x y)
  (vector tag x y))

;; just an abstraction. Might not be exported
(define (point-acc p idx)
  (if (point? p)
      (vector-ref p idx)
      (raise "not a point")))

(define (point-x p)
  (point-acc p idx-x))

(define (point-y p)
  (point-acc p idx-y))

现在,如果您查看记录类型的参考实现,您会发现它们使用向量,因此向量版本和 R6RSs 没有什么不同。

  1. 查找?您可以使用向量、列表或案例:

示例:

;; list is good for a few elements
(define ops `((+ . ,+) (- . ,-)))
(let ((found (assq '+ ops)))
  (if found 
      ((cdr found) 1 2)
      (raise "not found")))
; ==> 3 

;; case (switch)
((case '+
  ((+) +)
  ((-) -)
  (else (raise "not found"))) 1 2) ; ==> 3

当然,您在 SRFI-125 中有哈希表,因此对于大量元素,它可能是错误的。知道它大概是用vector来存储元素的:-)

为了回答你的问题,我想给你一个稍微大一点的评论可能会有所帮助。

Scheme 通常被描述为与其说是一种语言,不如说是一种语言家族。尤其是R5RS,还是很多人说"Scheme."

的意思

Scheme 家族中几乎每一种语言都有结构。我个人最熟悉 Racket,您可以在其中定义结构 structdefine-struct.

"But",您可能会说,"I want to write my program so that it runs in all versions of Scheme." 几个非常聪明的人已经成功地做到了这一点:Dorai Sitaram 和 Oleg Kiselyov 都浮现在脑海中。然而,我对他们工作的观察是,一般来说,在不牺牲性能的情况下保持与多个版本方案的兼容性通常需要高水平的宏观专业知识和大量认真思考。

确实有几个 SRFI 描述了结构设施。我个人对您的建议是选择一个 Scheme 实现,并让自己对使用它提供的任何结构设施感到满意。在某些方面,这与 Haskell 没有什么不同;有特定于 ghc 的功能,一般来说,我声称大多数 Haskell 程序员乐于使用这些功能,而不必担心它们在 Haskell.[=12= 的所有版本中都不起作用。 ]