Scheme中的数据结构

时间:2018-04-18 21:00:58

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

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

鉴于此,我有两个问题:

  1. 这是对的吗?我是否缺少允许创建自定义数据类型的功能?
  2. 如果没有,方案程序员如何构建程序?
  3. 例如,任何尝试返回多项数据的函数都需要某种方式来封装数据。使用哈希映射是最佳做法吗?

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

2 个答案:

答案 0 :(得分:4)

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

Scheme通常被描述为与一系列语言不同的单一语言。对于R5RS来说尤其如此,这仍然是许多人在说“方案”时的意思。

Scheme系列中几乎所有语言都有结构。我个人最熟悉Racket,您可以在其中定义结构 structdefine-struct

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

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

答案 1 :(得分:2)

  1. 绝对不是。 Scheme有几个自定义类型的SRFI,aka。记录类型,并且使用R7RS Red版本SRFI-136,但是因为你提到了R6RS has records defined in the standard too
  2. 使用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. Early Scheme(和lisp)没有记录类型,您通常会创建构造函数和访问器:
    2. 示例:

      (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. 查找?您可以使用矢量,列表或案例:
      2. 示例:

        ;; 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来存储元素: - )