所以我想知道是否有这样的标准代码:
(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宏。
如果方案不提供类似支持,那么其他方言(例如Common Lisp)是否允许此类支持?
答案 0 :(得分:3)
(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 < / em>。如果在顶层的文件中定义了函数,则在编译时,文件编译器会看到这是一个函数,并且可能会做一些特殊的事情,例如在编译时环境中注意签名,内联它。等
请注意,当Scheme中的DEFINE
定义了一个本地函数时,一个可能仍然可以执行(取决于实现对标准的附加作用):
(let ((x 10))
()
(define (add1) (+ x 1)))
请注意,在Common Lisp中,defun
定义了全局函数,而flet
/ labels
定义了局部函数。
答案 1 :(得分:3)
我认为,围绕define
包裹绑定的解决方案根本无法移植或安全地工作,因为包裹的define
会构造局部绑定(主体中的前导形式)或是非法的(体内的非领导形式),尽管我很乐意让Scheme-standards人士指出我错了。
相反,我认为类似这种有点讨厌的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
的常量,它表示“尚未正确定义”:Racket在racket/undefined
中提供了此常量,但通常可以是任何东西:您可以在某处说
(define undefined 'undefined)
例如。
诀窍是使用占位符值在顶层定义所需的内容,然后在let
内将其分配给它们。
我确定可以定义一个宏,并将其扩展为以下内容(这就是为什么整个内容都放在begin
中的原因):我没有这么做,因为它很简单,我使用了Racket,所以我无法轻松地在其中编写旧式Lisp宏。
请注意,现代方案中最明显的方法是使用define-values
:
(define-values (x y) (let (...) (values ...)))
做您想要的。如另一个答案所述,如果您只有多个值,则可以将define-values
作为宏来实现。但是,如果根本没有多个值,则可以使用根据结果的列表定义事物的事物:
(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
>
答案 2 :(得分:3)
在最新的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
,因此虽然它不是很优雅,但是却很脏。