2011-10-28 23 views
5

计划或任何计划的方言有一种“自我”运算符,以便匿名lambda可以自己重复发生,而不需要做像Y-组合器或在letrec中命名等。直接提及自己的匿名lambdas

喜欢的东西:

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

回答

9

号与“当前拉姆达”的做法麻烦的是,方案有很多隐藏 lambda表达式。例如:

  • 所有let形式(包括let*letrec,并命名为let
  • do(其扩展到一个命名let
  • delaylazyreceive

要求程序员知道最内层的lambda是什么会破坏封装,因为你会拥有要知道所有隐藏的lambda表达式在哪里,而宏观编写者不能再使用lambda表达式来创建新的范围。

如果你问我,全面输了。

+2

听到,听到。请注意,这对于“返回”也是一个问题,因为它出现在大多数语言中(Java,C等)。 –

+0

我确实相信在技术上,你可以在lambda表单中使'self'标识符词法化。如果你有'syntax-case',你甚至可以自己做。无论如何,我同意。明确的名称通常比照应用的更好。 –

+1

@Matthias:的确,“can”和“should”是完全不同的,特别是在这种情况下。 ;-)我在谈论为什么默认提供'self'是一个可怕的想法。 –

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 

大多数人避免指代操作符,因为它们使得代码的结构不易识别。另外,重构可以相当容易地引入问题。 (考虑当你在另一个alambda表格中将let/alambda表格中的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

你的'llambda'与[SRFI 31](http://srfi.schemers.org/srfi-31/srfi-31.html)的'rec'几乎相同,后者是构造的通常名称。 :-) –

+0

@ ChrisJester-Young啊,有趣。我应该更频繁地浏览SRFI列表。 ;)'rec'甚至似乎更普遍;实际上,它看起来像是古代LISP的“标签”操作符的泛化(当我阅读OP的问题时,这实际上是首先想到的)。 –

+1

两件事。首先,特别是在球拍上下文中,有一个[更好的方法]来实现这一点(http://blog.racket-lang.org/2008/02/dirty-looking-hygiene.html)。其次,你提到的指代宏指令的问题(隐式名称的嵌套范围)并不是最大的问题。 –

0

我发现使用延续到具有匿名lambda表达式自称,然后用球拍宏伪装的语法所以办法匿名lambda似乎有一个“自我”操作符。我不知道这个解决方案是否可以在其他版本的Scheme中使用,因为它依赖于球拍的Call-with-composable-continuation功能,并且宏用于隐藏语法使用语法参数。

基本思想是这样的,用阶乘函数来说明。

((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 ...)改造(KK 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不会中止回到继续提示符,而是在被调用的地方调用继续。

+0

嗯,但这与你的原始问题无关... –

+0

回顾一下原来的问题你是对的,但我认为这是一种模拟或添加一个“自我”操作符到lambda的方式,它看起来很酷这样做的方式。 –