Recursion 如何使用scheme lisp实现lambda演算的迭代?

Recursion 如何使用scheme lisp实现lambda演算的迭代?,recursion,scheme,lambda-calculus,mit-scheme,y-combinator,Recursion,Scheme,Lambda Calculus,Mit Scheme,Y Combinator,我正在学习lambda微积分和Scheme Lisp。关于lambda微积分的教程可以在这里找到 我面临的问题是我不知道如何正确地实现迭代 (define (Y y) (((lambda (x) (y (x x))) (lambda (x) (y (x x)))))) (define (sum r n) ((is_zero n) n0 (n succ (r (pred n))))) (display ((Y sum) n5)) 我总是会遇到这样的错误: 流产!:超过最大递归深度 我知道问题在于

我正在学习lambda微积分和Scheme Lisp。关于lambda微积分的教程可以在这里找到

我面临的问题是我不知道如何正确地实现迭代

(define (Y y) (((lambda (x) (y (x x))) (lambda (x) (y (x x))))))
(define (sum r n) ((is_zero n) n0 (n succ (r (pred n)))))
(display ((Y sum) n5))
我总是会遇到这样的错误:

流产!:超过最大递归深度

我知道问题在于求值顺序:方案首先解释
(Y sum)
,这导致无限递归:

((Y sum) n5) -> (sum (Y sum) n5) -> ((sum (sum (Y sum))) n5) -> .... (infinite recursion)
但是我想要

((Y sum) n5) -> ((sum (Y sum) n5) -> (n5 succ ((Y sum) n4)) -> .... (finite recursion)

我怎样才能解决这个问题?谢谢。

延迟计算的标准方法是eta扩展:

(define (Y y) ((lambda (x) (y (x x))) (lambda (x) (y (x x) )) ))
=~
(define (YC y) ((lambda (x) (y (lambda (z) ((x x) z))))
                (lambda (x) (y (lambda (z) ((x x) z)))) ))
因此

计算
(sum(lambda(z)((x x)z))
只使用包含自应用程序的lambda函数,但还没有调用它

扩展将切中要害

(n5 succ ((lambda (z) ((x x) z)) n4))
=
(n5 succ ((x x) n4))    where x = (lambda (x) (sum (lambda (z) ((x x) z))))
只有在这一点上,才会执行自我应用程序


因此,
(YC sum)
=
(sum(lambda(z)((YC sum)z))
,而不是发散(在应用的求值顺序下)
(Y sum)
(sum(Y sum))
在DrRacket中,您有一个方言
#lang lazy
,它与您得到的方案非常接近,但由于它的惰性,您有正常的顺序求值:

#!懒郎当
(定义(Y-f)
((λ(x)(x))
(lambda(x)(f(x)(()())))
(定义总和)
(Y)(兰姆达(右)
(λ(n)
((is_zero n)n0(n such(r(pred n‘‘‘‘‘‘‘‘‘‘‘‘)’))
(教堂->数字(总数n5))
如果您无法更改语言,可以将其包装在lambda中以使其延迟。例如

如果
r
是算术1的函数,而lambda演算中的所有函数都是,那么
(lambda(x)(rx))
r
的完美重构。它将停止infie递归,因为您只获得包装器,并且它仅在每次递归时应用它,即使求值是迫切的。在渴望的语言中,Y组合符称为Z:

(define (Z f) 
  ((lambda (x) (x x)) 
   (lambda (x) (f (lambda (d) ((x x) d))))))
如果要在Scheme中执行Z,例如使用多参数递归函数,则使用rest参数:

(define (Z f) 
  ((lambda (x) (x x)) 
   (lambda (x) (f (lambda args (apply (x x) args))))))

((Z (lambda (ackermann)
      (lambda (m n)
        (cond
          ((= m 0) (+ n 1))
          ((= n 0) (ackermann (- m 1) 1))
          (else (ackermann (- m 1) (ackermann m (- n 1))))))))
 3
 6) ; ==> 509

我记得有一次被纠正了,Lambda演算没有指定的评估策略——任何策略都可以采用。如果我能找到它,我会把它链接起来。@naomik Lambda微积分的执行是通过beta缩减,直到它不能再执行为止,所以我们达到了正常形式@naomik我很确定你在这里是正确的,没有固定的还原策略是LC能够首先调查各种还原策略属性的原因。比如说,“完全减少测试版…本质上意味着缺乏任何特定的减少策略”等等@Willenss,但这意味着,即使我们知道某个计划将以一种减少策略终止,也无法决定是否停止该计划?@Sylvester,我想这只是意味着减少策略的选择超出了LC本身。一个程序可以用一个选项终止,而不能用另一个选项终止。当然,一种给定的编程语言通常会有这样的选择。您好,我在Clojure-Last方案中遇到了完全相同的问题。我试过YC combinator,但仍然得到StackOverflower错误。我得出的结论是,如果不计算表达式,就不可能有递归。递归函数可能带有
if
语句。在clojure
中,如果
不是一个函数,那么它是一个特殊的关键字,当条件为true时,它不会计算第二个分支。但是,
如果
是lambda演算中的一个函数,其中分支是参数。Clojure急切地评估参数,其中一个是递归调用,因此StackOverflowError@DemeterPurjon否,在Scheme
中,如果
也不计算其所有参数<代码>阶乘中包含
if
,可以使用
YC
fine进行编码。此外,在LC(lambda演算)中,如果不需要,则不进行任何计算,这是“正常顺序计算”,即“按名称计算”(“lazy”)。你的代码是什么?你可以试着问你的问题,包括所有相关的代码:)期望的结果和真正发生的事情。如果你这样做,@ping我在这里,这样我就不会错过它了!:)谢谢@will ness!你的要求听起来比我的更合法。我会问你一个问题,然后告诉你:)谢谢你的意志。我问了一个新问题:。谢谢你的帮助@正如我所怀疑的,事实证明Clojure有自己的特殊问题,即缺乏尾部调用优化(我不使用Clojure)。希望你能从Clojure的专家那里得到帮助,在那里实现你自己的蹦床。TCO在方案下得到保证,YC代码依赖于此。
(define (Z f) 
  ((lambda (x) (x x)) 
   (lambda (x) (f (lambda args (apply (x x) args))))))

((Z (lambda (ackermann)
      (lambda (m n)
        (cond
          ((= m 0) (+ n 1))
          ((= n 0) (ackermann (- m 1) 1))
          (else (ackermann (- m 1) (ackermann m (- n 1))))))))
 3
 6) ; ==> 509