3

所以我想知道是否有标准的方式来编写这样的代码:

(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-macroand的 lisp 宏感兴趣quasiquote,请不要define-syntax,因为这主要用于我自己的 lisp(基于方案),我只有 lisp 宏。

如果方案不支持这样的事情,像 Common Lisp 这样的其他方言是否允许这样的事情?

4

3 回答 3

3

我认为没有任何解决方案define可以便携或安全地工作,因为 Wrappeddefine将构建本地绑定(正文中的前导形式)或非法(正文中的非前导形式),尽管我d 很高兴有一个计划标准的人指出我错在哪里。

相反,我认为像这种有点讨厌的黑客应该起作用。

(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.

我确定可以定义一个宏,它可以扩展为类似的东西(这就是为什么我将整个东西放在 a 中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
> 
于 2019-04-26T16:59:30.807 回答
3
(let ((x 10))
  (somemacro x))

->

(let ((x 10))
  (define (add1)
     (+ x 1))
  (define (add2)
     (+ x 2))
  (define (add3)
     (+ x 3)))

在 Common 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 中是有点糟糕的风格,因为文件编译器将不会识别出有全局函数声明,因为LETa内部DEFUN不是顶级的. 如果函数在顶级文件中定义,那么在编译时文件编译器会看到这是一个函数,并且可能会做一些特殊的事情,例如在编译时环境中记录签名,内联它。等等

请注意,当DEFINE在 Scheme 中定义了一个本地函数时,仍然可以做(取决于实现对标准的附加作用):

(let ((x 10))
  ()
  (define (add1) (+ x 1)))

请注意,在 Common Lisp 中defun定义了全局函数和flet/labels定义了局部函数。

于 2019-04-26T11:50:18.987 回答
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 报告中,您会发现define-values适用于 R6RS 和 R5RS 的语法规则。它使用call-with-values值传递到list然后define从那里传递的位置。我敢打赌它在lambdassucn 内部也可以工作,Scheme 实现实际上可以将其转换为letrecso,虽然它不是很优雅,但它会做一些肮脏的工作。

于 2019-04-26T22:11:13.707 回答