Давайте рассмотрим подсказку из книги.
Сначала мы определим нашу собственную версию or
:
(define-syntax my-or ; incorrect!
(syntax-rules ()
[(_) #f]
[(_ e1 e2 ...)
(let ([t e1])
(if t t (my-or e2 ...)))]))
Затем мы посмотрим на выражение в подсказке.
(letrec ([even?
(lambda (x)
(my-or (= x 0)
(odd? (- x 1))))]
[odd?
(lambda (x)
(and (not (= x 0))
(even? (- x 1))))])
(list (even? 20) (odd? 20)))
Давайте посмотрим на расширение (я немного отредактировал полное расширение):
(letrec ([even? (lambda (x)
(let ([t (= x 0)])
(if t t (let ([t (odd? (- x 1))])
(if t t #f)))))]
[odd? (lambda (x) (if (not (= x 0)) (even? (- x 1)) #f))])
(list (even? 20) (odd? 20)))
Проблема здесь в том, что вызов odd?
в (let ([t (odd? (- x 1))]) ...)
не находится в хвостовой позиции,Для каждого цикла выражение let
будет выделять новую переменную (в стеке или где-либо еще), и в конечном итоге у нас будет проблема с памятью.
Короче говоря: семантика or
заключается в том, что в (or e1 ... en)
последнее выражение en
находится в хвостовой позиции.Если мы используем простую версию макроса my-or
, то
(my-or e1)
расширяется до
(let ([t e1])
(if t t #f))]))
, а выражение e1
не находится в хвостовой позиции на выходе.