我开始学习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端口。我的方法next
和peek
应分别像read-char
和peek-char
一样工作。但每次我尝试运行它都会得到
next: undefined;
cannot reference undefined identifier
导致问题的原因是什么?这是创建像这样的对象的正确方法吗?另外,如何实现可选参数endl
?
答案 0 :(得分:1)
您尝试实现的想法通常称为消息传递。这通过创建闭包范围来实现。在此范围内定义了东西,默认情况下它是作用域的私有。在闭包结束时,返回一个调度程序函数。调度程序接收消息并从捕获的范围返回内容。通过这个私人的东西得到公开。通常返回函数。如果调用者获得一个函数,调用者可以使用任何参数调用该函数,该函数接受。
您的实施有一些美容问题和一些失败。
您应该采用单向定义功能。更详细的语法:
(define f (lambda () (display "Hello, World!\n")))
或更紧凑的语法:
(define (f) (display "Hello, World!\n"))
但不要混用它们。
首先,您必须将字符串转换为列表才能在序列上使用car
。这是通过函数string->list
完成的。您不能在字符串上使用car
。
然后你必须实施next
和peek
来做正确的事情。将它作为list
的别名是不够的。函数next
使用输入流中的字符。这意味着代码最能在某个地方改变一些状态。这需要使用set!
。
调度功能仅比较符号。将符号与case
而不是cond
进行比较会更容易。您也可以使用cond
,但输入更多内容。
字符序列通常仅限于闭包的范围。如果您通过seq
选择器将序列公开给调用范围,则违反了封装的想法。这通常仅用于调试。
如果将错误的消息传递给调度程序函数,则抛出错误而不是返回无用或未定义的内容是明智的。
根据上述建议显示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
希望它有所帮助。