匿名 lambda 直接引用自身

发布于 2024-12-12 09:15:00 字数 206 浏览 0 评论 0原文

cheme 或 doscheme 的任何方言是否有一种“self”运算符,以便匿名 lambda 可以自行重复,而无需执行类似 Y 组合器或在 letrec 等中命名的操作。

例如:

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

Does Scheme or do any dialects of scheme have a kind of "self" operator so that anonymous lambdas can recur on themselves without doing something like a Y-combinator or being named in a letrec etc.

Something like:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

Oo萌小芽oO 2024-12-19 09:15:00

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

  • 所有let 形式(包括let*letrec 和named let
  • do(扩展为命名的let
  • delaylazyreceive

。程序员知道最里面的 lambda 是什么会破坏封装,因为您必须知道所有隐藏的 lambda 表达式在哪里,并且宏编写者不能再使用 lambda 表达式作为创建新作用域的方式。

如果你问我的话,那就是全面失败。

No. The trouble with the "current lambda" approach is that Scheme has many hidden lambdas. For example:

  • All the let forms (including let*, letrec, and named let)
  • do (which expands to a named let)
  • delay, lazy, receive, etc.

To require the programmer to know what the innermost lambda is would break encapsulation, in that you'd have to know where all the hidden lambdas are, and macro writers can no longer use lambdas as a way to create a new scope.

All-round lose, if you ask me.

逆夏时光 2024-12-19 09:15:00

有编写“照应”宏的传统,这些宏在其主体的词汇范围内定义特殊名称。使用 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 的使用,特别是如果您没有通过显式键入来提醒它是相关的。)因此,通常最好使用显式名称。可以使用简单的 syntax-rules 宏来定义允许此操作的“标记”版本的 lambda:

;; 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

There is a tradition of writing “anaphoric” macros that define special names in the lexical scope of their bodies. Using syntax-case, you can write such a macro on top of letrec and lambda. Note that the definition below is as hygienic as possible considering the specification (in particular, invisible uses of alambda will not shadow 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

Most people avoid anaphoric operators since they make the structure of the code less recognizable. In addition, refactoring can introduce problems rather easily. (Consider what happens when you wrap the let/alambda form in the factorial function above in another alambda form. It's easy to overlook uses of self, especially if you're not reminded of it being relevant by having to type it explicitly.) It is therefore generally preferable to use explicit names. A “labeled” version of lambda that allows this can be defined using a simple syntax-rules macro:

;; 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
噩梦成真你也成魔 2024-12-19 09:15:00

我找到了一种方法,使用延续让匿名 lambda 调用自身,然后使用 Racket 宏来伪装语法,以便匿名 lambda 看起来有一个“self”运算符。我不知道这个解决方案在其他版本的Scheme中是否可行,因为它依赖于racket的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 是对匿名阶乘函数的调用,该函数有两个参数,第一个是延续本身。这样,当我们在主体中执行 (kk N) 时,这相当于匿名函数调用自身(与递归命名 lambda 的方式相同)。

然后我们用宏来伪装底层形式。 Rackets 语法参数允许将 (self ARGS ...) 转换为 (kk ARGS ...),

因此我们可以:

 ((lambda-with-self (n)
    (cond 
      [(= 0 n) 0]
      [(= 1 n) 1]
      [else (* n (self (- n 1)))])) 5)

执行此操作的完整 Racket 程序是:

#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)

这也回答了我之前关于不同类型之间差异的问题的延续。
Racket 中的不同类型的延续

这只有效,因为与 call-with-current 不同-continuation,call-with-composable-continuation 不会中止回到继续提示,而是在调用它的位置调用继续。

I have found a way using continuations to have anonymous lambdas call themselves and then using Racket macros to disguise the syntax so the anonymous lambda appears to have a "self" operator. I don't know if this solution is possible in other versions of Scheme since it depends on the Call-with-composable-continuation function of racket and the Macro to hide the syntax uses syntax parameters.

The basic idea is this, illustrated with the factorial function.

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

The continuation k is the call to the anonymous factorial function, which takes two arguments, the first being the continuation itself. So that when in the body we execute (k k N) that is equivalent to the anonymous function calling itself (in the same way that a recursive named lambda would do).

We then disguise the underlying form with a macro. Rackets syntax-parameters allow the transformation of (self ARGS ...) to (k k ARGS ... )

so we can have:

 ((lambda-with-self (n)
    (cond 
      [(= 0 n) 0]
      [(= 1 n) 1]
      [else (* n (self (- n 1)))])) 5)

The complete Racket program to do this is:

#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)

This also answers my previous question about the differences between the different kinds of continuations.
Different kinds of continuations in Racket

This only works because unlike call-with-current-continuation, call-with-composable-continuation doesn't abort back to a continuation prompt but invokes the continuation at the place it was invoked.

忘羡 2024-12-19 09:15:00

Racket 支持 SRFI-31,它提供了一个名为 的宏rec 用于制作可以调用自身的 lambda。基本示例:

#lang racket/base

(require srfi/31)

((rec (self n)
   (cond
     ((= n 0) 1)
     (else (* n (self (- n 1))))))
 5) ; => 120

Racket supports SRFI-31, which provides a macro named rec for making lambdas that can call themselves. Basic example:

#lang racket/base

(require srfi/31)

((rec (self n)
   (cond
     ((= n 0) 1)
     (else (* n (self (- n 1))))))
 5) ; => 120
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文