无法在方案中创建对象

时间:2017-10-16 05:37:40

标签: oop scheme racket

我开始学习Scheme(R5RS)并立即遇到问题。我有这段代码:

(define make-source (lambda (seq)
  (define next list)
  (define peek list)
  (let ((seq seq)
        ;(endl (if (null? endl) #f endl))
        )
  (lambda (selector data)
    (cond ((equal? selector 'seq) seq)
          ;((equal? selector "endl") endl)
          ((equal? selector 'next) (car seq))
          ((equal? selector 'peek) (list-ref seq 0))
          (else '()))))))


(define s (make-source "abc"))
(next s)

我要做的是创建可用于处理字符串,列表和向量的对象,例如IO端口。我的方法nextpeek应分别像read-charpeek-char一样工作。但每次我尝试运行它都会得到

next: undefined;
 cannot reference undefined identifier

导致问题的原因是什么?这是创建像这样的对象的正确方法吗?另外,如何实现可选参数endl

2 个答案:

答案 0 :(得分:1)

您尝试实现的想法通常称为消息传递。这通过创建闭包范围来实现。在此范围内定义了东西,默认情况下它是作用域的私有。在闭包结束时,返回一个调度程序函数。调度程序接收消息并从捕获的范围返回内容。通过这个私人的东西得到公开。通常返回函数。如果调用者获得一个函数,调用者可以使用任何参数调用该函数,该函数接受。

您的实施有一些美容问题和一些失败。

  1. 您应该采用单向定义功能。更详细的语法:

    (define f (lambda () (display "Hello, World!\n")))
    

    或更紧凑的语法:

    (define (f) (display "Hello, World!\n"))
    

    但不要混用它们。

  2. 首先,您必须将字符串转换为列表才能在序列上使用car。这是通过函数string->list完成的。您不能在字符串上使用car

  3. 然后你必须实施nextpeek来做正确的事情。将它作为list的别名是不够的。函数next使用输入流中的字符。这意味着代码最能在某个地方改变一些状态。这需要使用set!

  4. 调度功能仅比较符号。将符号与case而不是cond进行比较会更容易。您也可以使用cond,但输入更多内容。

  5. 字符序列通常仅限于闭包的范围。如果您通过seq选择器将序列公开给调用范围,则违反了封装的想法。这通常仅用于调试。

  6. 如果将错误的消息传递给调度程序函数,则抛出错误而不是返回无用或未定义的内容是明智的。

  7. 根据上述建议显示make-source的代码。

    (define (make-source seq)
    
      (let ((seq (string->list seq)))  ;; convert string into list
    
        (define (next)
          (if (pair? seq)
              (let ((c (car seq)))
                (set! seq (cdr seq))  ;; mutation of the sequence
                c)))
    
        (define (peek)
          (car seq))
    
        (define (endl arg1 arg2)  ;; use of the arguments
          (map list->string
               (map (lambda (x)
                      (list arg1 x arg2))
                    seq)))
    
        (lambda (selector)
          (case selector
            ((seq) seq) ;; this is only for debugging
            ;; for correct messages, just return the functions
            ((endl) endl)
            ((next) next)
            ((peek) peek)
            ;; throw error
            (else (error "undefined"))))))
    

    如果您致电make-source,则会生成调度程序功能:

    (define s (make-source "abc"))
    

    现在,您可以将符号形式的消息传递给调度程序。

    以下内容返回查看角色的功能。

    (s 'peek)  ;; => <procedure>
    

    为了执行它,你必须使用两次。

    ((s 'peek))  ;; => a
    

    如果您对双重申请感到不舒服,可以给它起一个名字。

    (define peek-s (s 'peek))
    (peek-s)    ;; => a
    

    但是你不能用这种方式定义泛型。泛型是一个名为peek的函数,而不是peek-s,并且可以从属于source个对象类的任何对象中查看。我们只定义了peek-s,它只从s对象中查看。为了定义泛型,您必须阅读Thant Tessman paper "Adding Generics to Scheme"。由于这个和其他限制,上述方法有时称为poor man's objects。但几乎每个Scheme都有一些受Common Lisp启发的对象系统,称为CLOS(Common Lisp Object System)或tiny CLOS

    最后将参数传递给公开函数就像将参数传递给函数一样简单。如果你有暴露函数,你可以传递函数接受的任何东西。

    ((s 'endl) #\< #\>)  ;; => ("<a>" "<b>" "<c>")
    

答案 1 :(得分:0)

您在> as.numeric("1e-14105") [1] 0 > class(as.numeric("1e-14105")) [1] "numeric" > 1e-14105 [1] 0 范围内定义next。当您调用make-source时,解释器会尝试在全局范围(全局环境)中查找(next s),而解释器找不到它,因为您定义next的范围为next }。这就是解释器告诉你“下一个未定义”的原因。

你的make-scope采用seq(我猜的是一个序列?)并返回一个带有参数的lambda表达式。 这就是我做的事情

make-scope

希望它有所帮助。