2017-10-16 110 views
0

我开始学习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

+1

'(make-source“abc”)'是一个函数。 “next”和“peek”的定义是“本地的”,并且只存在于“make-source”中,但是没有什么意义(如在REPL中定义它们并测试它们)。我怀疑你在计划书中跳过了很多内容,需要从第1章的某个地方开始。 – molbdnilo

+0

'(car seq)'的含义与'(list-ref seq 0)'相同。两者都不适用于字符串。 – molbdnilo

+0

@molbdnilo,谢谢你的回复。我以这个帖子为例https://stackoverflow.com/questions/2954642/methods-and-properties-in-scheme-is-oop-possible-in-scheme。你能提供代码来修复错误的例子吗? –

回答

0

您在make-source的范围内定义next。当您拨打(next s)时,解释器会尝试在全球范围(全球环境)中查找next,而解释器找不到它,因为您定义了next,范围为make-scope。这就是解释者告诉你“下一个未定义”的原因。

你的make-scope需要一个seq(一个序列我猜?)并返回一个带参数的lambda表达式。 这是我做的

(define s (make-source '(a b c)) 
(s 'next 1) 
; Output: 
; a 
(s 'seq 1) ; 1 could be replaced with any thing actually 
; Output 
; (a b c) 

希望它有帮助。

0

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

您的实施有一些化妆品问题和一些失败。

  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. 如果将错误消息传递给调度程序函数,那么抛出错误而不是返回无用或未定义的东西是明智的。

这显示了根据上述建议的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>")