【问题标题】:Data Structures in SchemeScheme中的数据结构
【发布时间】:2018-09-29 05:46:27
【问题描述】:

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

鉴于此,我有两个问题:

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

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

(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)))))))

【问题讨论】:

  • 是的,record 是方案名称。记录类型至少有五个 SRFI,9、57、76、99 和 136。
  • 但他们大多只是使用列表,并编写自己的函数来构造它们。
  • @Barmar 哦,这很有趣......如果你详细说明它是如何工作的,我会接受这个答案
  • 看起来你已经在这样做了。您的数据是一个正常的关联列表。 Scheme 具有用于处理它们的内置函数:gnu.org/software/mit-scheme/documentation/mit-scheme-ref/…

标签: data-structures functional-programming scheme lisp chicken-scheme


【解决方案1】:

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

Scheme 通常被描述为与其说是一种语言,不如说是一个语言家族。 R5RS 尤其如此,这仍然是许多人说“方案”时的意思。

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

“但是”,您可能会说,“我想编写我的程序,以便它可以在 所有 版本的 Scheme 中运行。”有几个非常聪明的人成功地做到了这一点:我想到了 Dorai Sitaram 和 Oleg Kiselyov。然而,我对他们的工作的观察是,一般来说,在不牺牲性能的情况下保持与许多版本的方案的兼容性通常需要高水平的宏观专业知识和大量的认真思考。

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

【讨论】:

  • 我绝对是在尝试“让它在所有方案上运行!!”我现在看到了那个错误。感谢您的解释 - 鉴于 Racket 的受欢迎程度,我想我会继续前进。
  • 请注意,您最初的选择,CHICKEN 内置了 SRFI-9,并且还有自己的 define-record 宏。除此之外,还有几个可用的鸡蛋,例如 defstruct 和 typed-records。
【解决方案2】:
  1. 绝对不是。 Scheme 有几个用于自定义类型的 SRFI,也就是。记录类型,R7RS Red edition 是SRFI-136,但既然你提到了R6RS,它就是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))

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

  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 中有哈希表,因此对于大量元素,它可能是副手。知道它可能使用向量来存储元素:-)

【讨论】:

    猜你喜欢
    • 2012-06-24
    • 2020-03-08
    • 2012-12-19
    • 1970-01-01
    • 2015-03-13
    • 1970-01-01
    • 2010-10-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多