【发布时间】:2013-04-21 22:02:59
【问题描述】:
我一直在研究禁止 use-before-def 并且没有可变单元格(没有 set! 或 setq)的语言如何提供递归。我当然遇到了(著名的?臭名昭著的?)Y 组合器和朋友,例如:
- http://www.ece.uc.edu/~franco/C511/html/Scheme/ycomb.html
- http://okmij.org/ftp/Computation/fixed-point-combinators.html
- http://www.angelfire.com/tx4/cus/combinator/birds.html
- http://en.wikipedia.org/wiki/Fixed-point_combinator
当我去实现这种风格的“letrec”语义时(也就是说,允许定义一个局部变量,使它可以是一个递归函数,在幕后它永远不会引用它自己的名字) ,我最后写的组合子是这样的:
Y_letrec = λf . (λx.x x) (λs . (λa . (f ((λx.x x) s)) a))
或者,分解出 U 组合子:
U = λx.x x
Y_letrec = λf . U (λs . (λa . (f (U s)) a))
读作:Y_letrec 是一个函数,它接受一个待递归函数f。
f 必须是接受s 的单参数函数,其中s 是函数
那f可以调用实现自递归。 f 预计定义并返回
执行“真实”操作的“内部”功能。那个内部函数接受
参数a(或者在一般情况下是参数列表,但无法表达
在传统符号中)。调用 Y_letrec 的结果是调用的结果
f,假定它是一个“内部”函数,可以调用。
我这样设置的原因是我可以使用解析树形式的 直接被递归的函数,无需修改,只是额外包装了一个 处理 letrec 时在转换过程中围绕它的功能层。例如,如果 原代码是:
(letrec ((foo (lambda (a) (foo (cdr a))))))
那么转换后的形式将是:
(define foo (Y_letrec (lambda (foo) (lambda (a) (foo (cdr a))))))
请注意,两者的内部函数体是相同的。
我的问题是:
- 我的 Y_letrec 函数常用吗?
- 它有一个知名的名字吗?
注意:上面的第一个链接引用了一个与“应用顺序 Y 组合器”类似的函数(在“步骤 5”中),尽管我很难找到该命名的权威来源。
2013 年 4 月 28 日更新:
我意识到上面定义的 Y_letrec 非常接近,但与 Wikipedia 中定义的 Z 组合子不同。根据 Wikipedia,Z 组合器和“按值调用 Y 组合器”是同一个东西,看起来这确实是更常被称为“应用顺序 Y 组合器”的东西。
所以,我上面的内容不与通常编写的应用顺序 Y 组合子相同,但几乎可以肯定它们之间存在某种关联。以下是我进行比较的方法:
开始于:
Y_letrec = λf . (λx.x x) (λs . (λa . (f ((λx.x x) s)) a))
应用内 U:
Y_letrec = λf . (λx.x x) (λs . (λa . (f (s s)) a))
应用外U:
Y_letrec = λf . (λs . (λa . (f (s s)) a)) (λs . (λa . (f (s s)) a))
重命名以匹配 Wikipedia 对 Z 组合子的定义:
Y_letrec = λf . (λx . (λv . (f (x x)) v)) (λx . (λv . (f (x x)) v))
将此与维基百科的 Z 组合器进行比较:
Z = λf . (λx . f (λv . ((x x) v))) (λx . f (λv . ((x x) v)))
显着的区别在于函数f 的应用位置。有关系吗?尽管存在这种差异,这两个功能是否等效?
【问题讨论】:
-
我记得,应用顺序与正常顺序是相反的。在应用顺序中,诸如 Scheme 参数之类的语言会在函数看到它们之前立即进行评估。这使得定义 Y 组合器变得复杂。在正常的顺序中,就像在传统的 lambda 演算中一样,参数被传递并且仅在没有其他选项时才被评估。 Y 组合器按正常顺序更简单,例如Hindley and Seldin 页。 34.
标签: scheme lisp combinators y-combinator anonymous-recursion