Scheme或者方言的任何方言都有一种“自我”操作符,这样匿名的lambdas就可以重复自己,而不需要像Y-combinator那样做或者在letrec等中命名。
类似的东西:
(lambda (n)
(cond
((= n 0) 1)
(else (* n (self (- n 1)))))))
答案 0 :(得分:9)
没有。 “当前lambda”方法的问题在于Scheme有许多隐藏的lambda。例如:
let
表单(包括let*
,letrec
和名为let
)do
(扩展为名为let
)delay
,lazy
,receive
等要求程序员知道最里面的lambda会破坏封装,因为你必须知道所有隐藏的lambda在哪里,而宏编写者不能再使用lambdas作为创建新范围的方法。
如果你问我,全面失败。答案 1 :(得分:6)
有一种传统是编写“照应”宏,在其体的词汇范围内定义特殊名称。使用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
大多数人避免使用过敏操作符,因为它们使代码结构不易识别。此外,重构可以很容易地引入问题。 (考虑当你将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
答案 2 :(得分:0)
我找到了一种方法,使用continuation让匿名lambdas调用自己然后使用Racket宏来伪装语法,这样匿名lambda似乎有一个“self”操作符。我不知道这个解决方案是否可以在其他版本的Scheme中使用,因为它依赖于racket的Call-with-composable-continuation函数,而Macro则隐藏语法使用语法参数。
基本思想是这个,用阶乘函数说明。
( (lambda (n)
(call-with-values
(lambda () (call-with-composable-continuation
(lambda (k) (values k n))))
(lambda (k n)
(cond
[(= 0 n) 1]
[else (* n (k k (- n 1)))])))) 5)
继续k是对匿名阶乘函数的调用,它带有两个参数,第一个是连续本身。因此,当我们在正文中执行(k k N)时,它等同于匿名函数调用自身(与递归命名的lambda一样)。
然后我们用宏来掩饰底层形式。球拍语法参数允许(自身ARGS ...)转换为(k k ARGS ...)
所以我们可以:
((lambda-with-self (n)
(cond
[(= 0 n) 0]
[(= 1 n) 1]
[else (* n (self (- n 1)))])) 5)
完成这项任务的完整球拍计划是:
#lang racket
(require racket/stxparam) ;required for syntax-parameters
( define-syntax-parameter self (λ (stx) (raise-syntax-error #f "not in `lambda-with-self'" stx)))
(define-syntax-rule
(lambda-with-self (ARG ... ) BODY ...)
(lambda (ARG ...)
(call-with-values
(lambda ()(call/comp (lambda (k) (values k ARG ...))))
(lambda (k ARG ...)
(syntax-parameterize ([self (syntax-rules ( )[(self ARG ...) (k k ARG ...)])])
BODY ...)))))
;Example using factorial function
((lambda-with-self (n)
(cond
[(= 0 n) 0]
[(= 1 n) 1]
[else (* n (self (- n 1)))])) 5)
这也回答了我之前关于不同类型延续之间差异的问题。 Different kinds of continuations in Racket
这只能起作用,因为与call-with-current-continuation不同,call-with-composable-continuation不会中止返回到continuation提示符,而是在调用它的位置调用continuation。