Lambda (self-self)用严格的语言在let语句中调用
我现在正在进行Y-combinator的学习。Lambda (self-self)用严格的语言在let语句中调用,lambda,scheme,lisp,let,y-combinator,Lambda,Scheme,Lisp,Let,Y Combinator,我现在正在进行Y-combinator的学习。 在Y-组合子推导过程中,此代码: (define (part-factorial self) (lambda (n) (if (= n 0) 1 (* n ((self self) (- n 1)))))) ((part-factorial part-factorial) 5) ==> 120 (define factorial (part-factorial part-factorial)) (fac
在Y-组合子推导过程中,此代码:
(define (part-factorial self)
(lambda (n)
(if (= n 0)
1
(* n ((self self) (- n 1))))))
((part-factorial part-factorial) 5) ==> 120
(define factorial (part-factorial part-factorial))
(factorial 5) ==> 120
旨在:
(define (part-factorial self)
(let ((f (self self)))
(lambda (n)
(if (= n 0)
1
(* n (f (- n 1)))))))
(define factorial (part-factorial part-factorial))
(factorial 5) ==> 120
此后,该条规定:
这在懒惰的语言中可以很好地工作。在严格的语言中,let语句中的(self-self)
调用将把我们送入一个无限循环,因为为了计算(部分阶乘部分阶乘)
(在阶乘的定义中),您首先必须计算(部分阶乘部分阶乘)(在let
表达式中)
然后读者受到挑战:
有趣的是:找出为什么之前的定义没有问题
在我看来,我已经找到了原因,尽管我想确认:
我的理解是:在第一个代码片段中,
(self-self)
调用不会导致无限循环,因为它作为一个部分阶乘
函数包含(包装)在lambda
中,并因此求值为lambda(n)
,直到实际进行了对(self-self)
的调用,这只在n>0
时发生。因此,在(=n0)
计算为#t
后,没有必要调用(self-self)
是的,这是正确的答案。事实上,在为应用程序顺序语言定义Y时,这个技巧(包装可能在lambda中递归的内容)是至关重要的,我认为他的文章谈到了这一点(顺便说一下,这是一篇好文章)。是的,这是正确的答案。事实上,在为应用程序顺序语言定义Y时,这个技巧(包装一些本来会在lambda中递归的东西)是至关重要的,我认为他的文章谈到了这一点(顺便说一下,这是一篇好文章)。是的,第二个定义中的“忽略lambda”
(define (part-factorial self)
(let ((f (self self))) ; let above the
(lambda (n) ; lambda
(if (= n 0)
1
(* n (f (- n 1)))))))
导致在返回(lambda(n)…
之前触发应用程序(self-self)
这建议了另一种避免循环的方法:将有问题的自我应用程序本身放在自己的lambda
后面:
(define (part-factorial self)
(let ((f (lambda (a) ((self self) a))))
(lambda (n)
(if (= n 0)
1
(* n (f (- n 1)))))))
现在这也起作用了。它被称为“eta扩展”(或者通常称为“eta转换”,因为它的对偶是“eta收缩”)
这就是通常的“应用顺序Y组合子”定义中实际使用的方法
在第一个定义中,(self-self)
应用程序只有在实际需要其结果时才会触发-但它的代价是我们必须以某种“不自然”的风格编写它,这在任何情况下都不同于我们想要编写的风格,也就是说,只需使用f
来指代我们在幕后以某种方式进行递归的函数
有了明确的自我应用,负担就在我们身上,而我们人类也会犯错。毕竟,犯错是人类的行为,就像宽恕是神圣的;但我们的电脑还没有完全处于宽容的阶段
所以,这就是为什么Y。让我们写得直截了当,不必担心,把细节考虑清楚,安全地抽象出来
不应再提及明确自我应用的负担。是的,第二个定义中的“让渡lambda”
(define (part-factorial self)
(let ((f (self self))) ; let above the
(lambda (n) ; lambda
(if (= n 0)
1
(* n (f (- n 1)))))))
导致在返回(lambda(n)…
之前触发应用程序(self-self)
这建议了另一种避免循环的方法:将有问题的自我应用程序本身放在自己的lambda
后面:
(define (part-factorial self)
(let ((f (lambda (a) ((self self) a))))
(lambda (n)
(if (= n 0)
1
(* n (f (- n 1)))))))
现在这也起作用了。它被称为“eta扩展”(或者通常称为“eta转换”,因为它的对偶是“eta收缩”)
这就是通常的“应用顺序Y组合子”定义中实际使用的方法
在第一个定义中,(self-self)
应用程序只有在实际需要其结果时才会触发-但它的代价是我们必须以某种“不自然”的风格编写它,这在任何情况下都不同于我们想要编写的风格,也就是说,只需使用f
来指代我们在幕后以某种方式进行递归的函数
有了明确的自我应用,负担就在我们身上,而我们人类也会犯错。毕竟,犯错是人类的行为,就像宽恕是神圣的;但我们的电脑还没有完全处于宽容的阶段
所以,这就是为什么Y。让我们写得直截了当,不必担心,把细节考虑清楚,安全地抽象出来
而明确的自我应用的负担将不再被提及。heavens@naomik你的意思是……)@naomik但是说真的,Y是非常简单的(好吧,至少是可以理解的),如果用适当的动机例子逐步呈现的话。真的让我很生气,因为无数的陈述都是关于它的,完全不可理解的,出乎意料的,没有任何解释。人们为什么这样做?还是因为我运气不好?:)/大智慧heavens@naomik你的意思是……)@naomik但是说真的,Y是非常简单的(好吧,至少是可以理解的),如果用适当的动机例子逐步呈现的话。真的让我很生气,因为无数的陈述都是关于它的,完全不可理解的,出乎意料的,没有任何解释。人们为什么这样做?还是因为我运气不好?:)/排气