在方案中定义全局闭包的标准方法是什么?
What is standard way of defining global closures in scheme?
所以我想知道是否有这样的代码的标准方法:
(let ((x 10))
(define (add10 a)
(+ x a)))
我知道:
(define add10 (let ((x 10))
(lambda (a) (+ x a))))
但是如果我想定义多个函数,这将不起作用,我需要知道标准方法,这样我才能编写定义函数的宏。你可以在里面调用宏 let:
(let ((x 10))
(macro x))
例如,宏将创建函数列表:
(let ((x 10))
(define (add1)
(+ x 1))
(define (add2)
(+ x 2))
(define (add3)
(+ x 3)))
是否有定义函数 add1..add3 的标准方法?在我测试的方案中,这些函数将在 let
内部是本地的,并且在外部无法访问。
如果您显示宏代码,我只对带有 define-macro
和 quasiquote
的 lisp 宏感兴趣,请不要 define-syntax
,因为这主要用于我自己的 lisp (基于方案)我只有 lisp 宏。
如果 scheme 不支持这样的东西,其他方言如 Common Lisp 是否允许这样的东西?
(let ((x 10))
(somemacro x))
->
(let ((x 10))
(define (add1)
(+ x 1))
(define (add2)
(+ x 2))
(define (add3)
(+ x 3)))
在普通 Lisp 中:
CL-USER 43 > (defmacro somemacro (var)
`(progn
(defun add1 () (+ ,var 1))
(defun add2 () (+ ,var 2))
(defun add3 () (+ ,var 3))))
SOMEMACRO
CL-USER 44 > (let ((x 10))
(somemacro x))
ADD3
CL-USER 45 > (add1)
11
CL-USER 46 > (add3)
13
人们有时会看到。一般来说,这在 Common Lisp 中是有点糟糕的风格,因为文件编译器将不会识别出全局函数声明,因为在 LET
内部 DEFUN
不在 top-等级。如果一个函数在文件中定义在 顶层 ,那么在编译时文件编译器会看到这是一个函数,并且可能会做一些特殊的事情,比如在编译时记录签名 -时间环境,内联它。等等
请注意,当 Scheme 中的 DEFINE
定义了一个局部函数时,仍然 可能 能够做到(取决于实现对标准的补充):
(let ((x 10))
()
(define (add1) (+ x 1)))
请注意,在 Common Lisp 中,defun
定义全局函数,flet
/ labels
定义局部函数。
我认为围绕 define
包装绑定的解决方案根本无法移植或安全地工作,因为包装的 define
将构建本地绑定(正文中的前导形式)或是非法的(正文中的非前导形式),尽管我很乐意让符合 Scheme 标准的人指出我错在哪里。
相反,我认为像这种有点讨厌的 hack 应该有用。
(begin
(define inc undefined)
(define dec undefined)
(let ((x 3))
(set! inc (lambda (y)
(set! x (+ x y))
x))
(set! dec (lambda (y)
(set! x (- x y))
x))))
这里我依赖于一个名为 undefined
的常量,这意味着 'not yet defined properly':Racket 在 racket/undefined
中提供了这个,但它通常可以是任何东西:你可以在某个地方,说
(define undefined 'undefined)
例如。
诀窍是用占位符值在顶层定义你想要的东西,然后在 let
.
中分配给它们
我确定可以定义一个宏,它可以扩展为类似的东西(这就是为什么我将整个东西都放在 begin
中):我没有这样做,因为它很笨拙,而且我使用 Racket所以我不能轻易地在里面写老式的 Lisp 宏。
请注意,现代方案中显而易见的方法是使用 define-values
:
(define-values (x y) (let (...) (values ...)))
做你想做的事。如另一个答案中所述,如果您只有多个值,则可以将 define-values
实现为宏。但是,如果您根本没有多个值,那么您可以使用基于 list 结果定义事物的东西:
(define-list (x y) (let (...) (list ...)))
这是该宏的两个粗略变体:第一个使用 Racket 的本机宏:
(require racket/undefined)
(define-syntax define-list
(syntax-rules ()
[(define-list () expr)
(let ((results expr))
(unless (zero? (length results))
(error "need an empty list"))
(void))]
[(define-list (name ...) expr)
(begin
(define name undefined)
...
(let ([results expr])
(unless (= (length results)
(length '(name ...)))
(error "wrong number of values"))
(set! name (begin0
(car results)
(set! results (cdr results))))
...))]))
而第二个在 Racket 中使用不卫生的宏:
(require compatibility/defmacro
racket/undefined)
(define-macro (define-list names expr)
`(begin
,@(let loop ([ntail names] [defs '()])
(if (null? ntail)
(reverse defs)
(loop (cdr ntail)
(cons `(define ,(car ntail) undefined) defs))))
(let ([results ,expr])
(unless (= (length results)
(length ',names))
(error "wrong number of values"))
,@(let loop ([ntail names] [i 0] [assignments '()])
(if (null? ntail)
(reverse assignments)
(loop (cdr ntail) (+ i 1)
(cons `(set! ,(car ntail) (list-ref results ,i))
assignments)))))))
请注意,这两种方法都没有经过测试,我需要花点时间说服自己第二种方法足够卫生。
但是有了这些:
> (define-list (inc dec)
(let ([i 0])
(list
(lambda ()
(set! i (+ i 1))
i)
(lambda ()
(set! i (- i 1))
i))))
> inc
#<procedure>
> (inc)
1
> (dec)
0
> (dec)
-1
>
在最新的 Scheme 报告 R7RS 中,我们有 define-values
。可以这样使用:
(define-values (add1 add2 add3)
(let ((x 10))
(values (lambda () (+ x 1))
(lambda () (+ x 2))
(lambda () (+ x 3)))))
当然,对于较大的块,可能需要进行局部定义并在 values
中引用它。
在 R7RS 报告中,您会找到适用于 R6RS 和 R5RS 的 define-values
语法规则。它使用 call-with-values
将值传递给 list
,然后从那里传递 define
。我敢打赌它也在 lambdas
sucn 内部工作,Scheme 实现实际上可以将其转换为 letrec
所以虽然它不是很优雅但它完成了肮脏的工作。
所以我想知道是否有这样的代码的标准方法:
(let ((x 10))
(define (add10 a)
(+ x a)))
我知道:
(define add10 (let ((x 10))
(lambda (a) (+ x a))))
但是如果我想定义多个函数,这将不起作用,我需要知道标准方法,这样我才能编写定义函数的宏。你可以在里面调用宏 let:
(let ((x 10))
(macro x))
例如,宏将创建函数列表:
(let ((x 10))
(define (add1)
(+ x 1))
(define (add2)
(+ x 2))
(define (add3)
(+ x 3)))
是否有定义函数 add1..add3 的标准方法?在我测试的方案中,这些函数将在 let
内部是本地的,并且在外部无法访问。
如果您显示宏代码,我只对带有 define-macro
和 quasiquote
的 lisp 宏感兴趣,请不要 define-syntax
,因为这主要用于我自己的 lisp (基于方案)我只有 lisp 宏。
如果 scheme 不支持这样的东西,其他方言如 Common Lisp 是否允许这样的东西?
(let ((x 10))
(somemacro x))
->
(let ((x 10))
(define (add1)
(+ x 1))
(define (add2)
(+ x 2))
(define (add3)
(+ x 3)))
在普通 Lisp 中:
CL-USER 43 > (defmacro somemacro (var)
`(progn
(defun add1 () (+ ,var 1))
(defun add2 () (+ ,var 2))
(defun add3 () (+ ,var 3))))
SOMEMACRO
CL-USER 44 > (let ((x 10))
(somemacro x))
ADD3
CL-USER 45 > (add1)
11
CL-USER 46 > (add3)
13
人们有时会看到。一般来说,这在 Common Lisp 中是有点糟糕的风格,因为文件编译器将不会识别出全局函数声明,因为在 LET
内部 DEFUN
不在 top-等级。如果一个函数在文件中定义在 顶层 ,那么在编译时文件编译器会看到这是一个函数,并且可能会做一些特殊的事情,比如在编译时记录签名 -时间环境,内联它。等等
请注意,当 Scheme 中的 DEFINE
定义了一个局部函数时,仍然 可能 能够做到(取决于实现对标准的补充):
(let ((x 10))
()
(define (add1) (+ x 1)))
请注意,在 Common Lisp 中,defun
定义全局函数,flet
/ labels
定义局部函数。
我认为围绕 define
包装绑定的解决方案根本无法移植或安全地工作,因为包装的 define
将构建本地绑定(正文中的前导形式)或是非法的(正文中的非前导形式),尽管我很乐意让符合 Scheme 标准的人指出我错在哪里。
相反,我认为像这种有点讨厌的 hack 应该有用。
(begin
(define inc undefined)
(define dec undefined)
(let ((x 3))
(set! inc (lambda (y)
(set! x (+ x y))
x))
(set! dec (lambda (y)
(set! x (- x y))
x))))
这里我依赖于一个名为 undefined
的常量,这意味着 'not yet defined properly':Racket 在 racket/undefined
中提供了这个,但它通常可以是任何东西:你可以在某个地方,说
(define undefined 'undefined)
例如。
诀窍是用占位符值在顶层定义你想要的东西,然后在 let
.
我确定可以定义一个宏,它可以扩展为类似的东西(这就是为什么我将整个东西都放在 begin
中):我没有这样做,因为它很笨拙,而且我使用 Racket所以我不能轻易地在里面写老式的 Lisp 宏。
请注意,现代方案中显而易见的方法是使用 define-values
:
(define-values (x y) (let (...) (values ...)))
做你想做的事。如另一个答案中所述,如果您只有多个值,则可以将 define-values
实现为宏。但是,如果您根本没有多个值,那么您可以使用基于 list 结果定义事物的东西:
(define-list (x y) (let (...) (list ...)))
这是该宏的两个粗略变体:第一个使用 Racket 的本机宏:
(require racket/undefined)
(define-syntax define-list
(syntax-rules ()
[(define-list () expr)
(let ((results expr))
(unless (zero? (length results))
(error "need an empty list"))
(void))]
[(define-list (name ...) expr)
(begin
(define name undefined)
...
(let ([results expr])
(unless (= (length results)
(length '(name ...)))
(error "wrong number of values"))
(set! name (begin0
(car results)
(set! results (cdr results))))
...))]))
而第二个在 Racket 中使用不卫生的宏:
(require compatibility/defmacro
racket/undefined)
(define-macro (define-list names expr)
`(begin
,@(let loop ([ntail names] [defs '()])
(if (null? ntail)
(reverse defs)
(loop (cdr ntail)
(cons `(define ,(car ntail) undefined) defs))))
(let ([results ,expr])
(unless (= (length results)
(length ',names))
(error "wrong number of values"))
,@(let loop ([ntail names] [i 0] [assignments '()])
(if (null? ntail)
(reverse assignments)
(loop (cdr ntail) (+ i 1)
(cons `(set! ,(car ntail) (list-ref results ,i))
assignments)))))))
请注意,这两种方法都没有经过测试,我需要花点时间说服自己第二种方法足够卫生。
但是有了这些:
> (define-list (inc dec)
(let ([i 0])
(list
(lambda ()
(set! i (+ i 1))
i)
(lambda ()
(set! i (- i 1))
i))))
> inc
#<procedure>
> (inc)
1
> (dec)
0
> (dec)
-1
>
在最新的 Scheme 报告 R7RS 中,我们有 define-values
。可以这样使用:
(define-values (add1 add2 add3)
(let ((x 10))
(values (lambda () (+ x 1))
(lambda () (+ x 2))
(lambda () (+ x 3)))))
当然,对于较大的块,可能需要进行局部定义并在 values
中引用它。
在 R7RS 报告中,您会找到适用于 R6RS 和 R5RS 的 define-values
语法规则。它使用 call-with-values
将值传递给 list
,然后从那里传递 define
。我敢打赌它也在 lambdas
sucn 内部工作,Scheme 实现实际上可以将其转换为 letrec
所以虽然它不是很优雅但它完成了肮脏的工作。