Scheme或者方言的任何方言都有一种"自我"操作符,这样匿名的lambdas就可以重复自己,而不需要像Y-combinator那样做或者在letrec等中命名.
就像是:
(lambda (n)
(cond
((= n 0) 1)
(else (* n (self (- n 1)))))))
Run Code Online (Sandbox Code Playgroud)
没有."当前lambda"方法的问题在于Scheme有许多隐藏的lambda.例如:
let形式(包括let*,letrec和命名let)do(扩展为命名let)delay,lazy,receive,等.要求程序员知道最里面的lambda会破坏封装,因为你必须知道所有隐藏的lambda在哪里,而宏编写器不能再使用lambdas作为创建新范围的方法.
如果你问我,全能输.
编写"照应"宏的传统是在他们身体的词汇范围内定义特殊名称.使用syntax-case,您可以在letrec和之上编写这样的宏lambda.请注意,考虑到规范,下面的定义尽可能卫生(特别是alambda不会影子的隐形使用self).
;; Define a version of lambda that binds the
;; anaphoric variable “self” to the function
;; being defined.
;;
;; Note the use of datum->syntax to specify the
;; scope of the anaphoric identifier.
(define-syntax alambda
(lambda (stx)
(syntax-case stx ()
[(alambda lambda-list . body)
(with-syntax ([name (datum->syntax #'alambda 'self)])
#'(letrec ([name (lambda lambda-list . body)])
name))])))
;; We can define let in terms of alambda as usual.
(define-syntax let/alambda
(syntax-rules ()
[(_ ((var val) ...) . body)
((alambda (var ...) . body) val ...)]))
;; The let/alambda macro does not shadow the outer
;; alambda's anaphoric variable, which is lexical
;; with regard to the alambda form.
((alambda (n)
(if (zero? n)
1
(let/alambda ([n-1 (- n 1)])
(* (self n-1) n))))
10)
;=> 3628800
Run Code Online (Sandbox Code Playgroud)
大多数人避免使用过敏操作符,因为它们使代码结构不易识别.此外,重构可以很容易地引入问题.(考虑当你let/alambda在另一种alambda形式的上面的阶乘函数中包装表单时会发生什么.很容易忽略它的用法self,特别是如果你不必通过必须明确地输入它来提醒它是相关的.)因此通常优先考虑使用显式名称.一个"标记"版本lambda允许这可以使用一个简单的syntax-rules宏定义:
;; Define a version of lambda that allows the
;; user to specifiy a name for the function
;; being defined.
(define-syntax llambda
(syntax-rules ()
[(_ name lambda-list . body)
(letrec ([name (lambda lambda-list . body)])
name)]))
;; The factorial function can be expressed
;; using llambda.
((llambda fac (n)
(if (zero? n)
1
(* (fac (- n 1)) n)))
10)
;=> 3628800
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
434 次 |
| 最近记录: |