匿名的lambdas直接指自己

时间:2011-10-28 23:57:49

标签: scheme racket

Scheme或者方言的任何方言都有一种“自我”操作符,这样匿名的lambdas就可以重复自己,而不需要像Y-combinator那样做或者在letrec等中命名。

类似的东西:

(lambda (n)
   (cond
     ((= n 0) 1)
     (else (* n (self (- n 1)))))))

3 个答案:

答案 0 :(得分:9)

没有。 “当前lambda”方法的问题在于Scheme有许多隐藏的lambda。例如:

  • 所有let表单(包括let*letrec和名为let
  • do(扩展为名为let
  • delaylazyreceive

要求程序员知道最里面的lambda会破坏封装,因为你必须知道所有隐藏的lambda在哪里,而宏编写者不能再使用lambdas作为创建新范围的方法。

如果你问我,全面失败。

答案 1 :(得分:6)

有一种传统是编写“照应”宏,在其体的词汇范围内定义特殊名称。使用syntax-case,您可以在letreclambda之上编写此类宏。请注意,考虑到规范,下面的定义尽可能卫生(特别是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。