匿名 lambda 直接引用自身
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
不。“当前 lambda”方法的问题在于,Scheme 有许多隐藏的 lambda。例如:
let
形式(包括let*
、letrec
和namedlet
)do
(扩展为命名的let
)delay
、lazy
、receive
等。程序员知道最里面的 lambda 是什么会破坏封装,因为您必须知道所有隐藏的 lambda 表达式在哪里,并且宏编写者不能再使用 lambda 表达式作为创建新作用域的方式。
如果你问我的话,那就是全面失败。
No. The trouble with the "current lambda" approach is that Scheme has many hidden lambdas. For example:
let
forms (includinglet*
,letrec
, and namedlet
)do
(which expands to a namedlet
)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.
有编写“照应”宏的传统,这些宏在其主体的词汇范围内定义特殊名称。使用
syntax-case
,您可以在letrec
和lambda
之上编写这样的宏。请注意,考虑到规范,下面的定义尽可能卫生(特别是,alambda
的不可见使用不会影响self
)。大多数人避免使用照应运算符,因为它们使代码的结构难以识别。此外,重构很容易引入问题。 (考虑一下当您将
let/alambda
形式包装在另一个alambda
形式的阶乘函数中时会发生什么。很容易忽略self
的使用,特别是如果您没有通过显式键入来提醒它是相关的。)因此,通常最好使用显式名称。可以使用简单的syntax-rules
宏来定义允许此操作的“标记”版本的 lambda: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 ofletrec
andlambda
. Note that the definition below is as hygienic as possible considering the specification (in particular, invisible uses ofalambda
will not shadowself
).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 anotheralambda
form. It's easy to overlook uses ofself
, 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 oflambda
that allows this can be defined using a simplesyntax-rules
macro:我找到了一种方法,使用延续让匿名 lambda 调用自身,然后使用 Racket 宏来伪装语法,以便匿名 lambda 看起来有一个“self”运算符。我不知道这个解决方案在其他版本的Scheme中是否可行,因为它依赖于racket的Call-with-composable-continuation函数和隐藏语法的宏使用语法参数。
基本思想是这样的,用阶乘函数来说明。
延续 k 是对匿名阶乘函数的调用,该函数有两个参数,第一个是延续本身。这样,当我们在主体中执行 (kk N) 时,这相当于匿名函数调用自身(与递归命名 lambda 的方式相同)。
然后我们用宏来伪装底层形式。 Rackets 语法参数允许将 (self ARGS ...) 转换为 (kk ARGS ...),
因此我们可以:
执行此操作的完整 Racket 程序是:
这也回答了我之前关于不同类型之间差异的问题的延续。
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.
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:
The complete Racket program to do this is:
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.
Racket 支持 SRFI-31,它提供了一个名为
的宏rec
用于制作可以调用自身的 lambda。基本示例:Racket supports SRFI-31, which provides a macro named
rec
for making lambdas that can call themselves. Basic example: