Обычный Y комбинатор порядка, здесь вычисляется (ack 3 6)
в Lazy Racket:
#lang lazy
(define Y
(lambda (f)
((lambda (g) (g g))
(lambda (g) (f (g g))))))
((Y (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
Схема не является ленивым языком, как Lazy Racket, поэтому Y должен немного отличаться.Теперь он называется комбинатором Z:
#!r6rs
(import (rnrs base))
(define Z
(lambda (f)
((lambda (g)
(f (g g)))
(lambda (g)
(f (lambda args (apply (g g) 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
Использование delay
и force
на самом деле не делает его лучше:
#!r6rs
(import (rnrs base)
(rnrs r5rs))
(define DY
(lambda (f)
((lambda (g) (g g))
(lambda (g) (f (delay (g g)))))))
((DY (lambda (d-ackermann)
(lambda (m n)
(cond
((= m 0) (+ n 1))
((= n 0) ((force d-ackermann) (- m 1) 1))
(else ((force d-ackermann) (- m 1) ((force d-ackermann) m (- n 1))))))))
3
6) ; ==> 509
Обычно Y
и Z
позволяютмы называем нашу рекурсивную процедуру, но в этой последней мы получаем обещание, которое нам необходимо решить, и, таким образом, мы пропускаем детали реализации, и ее становится все труднее использовать.Обычно при использовании обещаний мы хотим избежать ненужного выполнения, но тогда вызов должен вернуть обещание.