How to implement iteration of lambda calculus using scheme lisp?

我的未来我决定 提交于 2019-12-11 08:04:38

问题


I'm trying to learn lambda calculus and Scheme Lisp. The tutorial on lambda calculus can be found here http://www.inf.fu-berlin.de/lehre/WS03/alpi/lambda.pdf.

The problem I'm facing is I don't know how to properly implement iteration.

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

I always get this error:

Aborting!: maximum recursion depth exceeded

I know the problem is about the evaluation order: the scheme interprets (Y sum) first, which results in infinite recursion:

((Y sum) n5) -> (sum (Y sum) n5) -> ((sum (sum (Y sum))) n5) -> .... (infinite recursion)

but I want

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

How can I solve this problem? thanks.


回答1:


Lambda calculus is a normal order evaluating language. Thus it has more in common with Haskell than Scheme, which is an applicative order evaluating language.

In DrRacket you have a dialect #lang lazy, which is as close to Scheme you get, but since its lazy you have normal order evaluation:

#!lang lazy

(define (Y f) 
  ((lambda (x) (x x)) 
   (lambda (x) (f (x x)))))

(define sum
  (Y (lambda (r)
       (lambda (n)
         ((is_zero n) n0 (n succ (r (pred n))))))))

(church->number (sum n5))

If you cannot change the language you can just wrap it in a lambda to get it delayed. eg.

if r is a function of arity 1, which all functions in lambda calculus is, then (lambda (x) (r x)) is a perfectly ok refactoring of r. It will halt the infitie recursion since you only get the wrapper and it only applies it every time you recurse even if the evaluation is eager. Y combinator in an eager language is called Z:

(define (Z f) 
  ((lambda (x) (x x)) 
   (lambda (x) (f (lambda (d) ((x x) d))))))

If you want to do Z in Scheme, eg with multiple argument recursive functions you use rest arguments:

(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



回答2:


The standard way to delay a computation is by eta-expansion:

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

Thus

((YC sum) n5) 
=
  (let* ((y sum)
         (x (lambda (x) (y (lambda (z) ((x x) z)))) ))
    ((y (lambda (z) ((x x) z))) n5))
= 
  (let ((x (lambda (x) (sum (lambda (z) ((x x) z)))) ))
    ((sum (lambda (z) ((x x) z))) n5))
= 
  ...

and evaluating (sum (lambda (z) ((x x) z))) just uses the lambda-function which contains the self-application, but doesn't invoke it yet.

The expansion will get to the point

(n5 succ ((lambda (z) ((x x) z)) n4))
=
(n5 succ ((x x) n4))    where x = (lambda (x) (sum (lambda (z) ((x x) z))))

and only at that point will the self-application be performed.

Thus, (YC sum) = (sum (lambda (z) ((YC sum) z))), instead of the diverging (under the applicative order of evaluation) (Y sum) = (sum (Y sum)).



来源:https://stackoverflow.com/questions/45931279/how-to-implement-iteration-of-lambda-calculus-using-scheme-lisp

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!