зачем определять синтаксис или схему нужно учитывать три условия? - PullRequest
0 голосов
/ 01 июня 2018

Я читаю язык программирования схемы , в главе 3 книга использует синтаксис define для определения или и и и процедуры, и там говорится следующее определение или неверно:

(define-syntax or ; incorrect!
  (syntax-rules ()
    [(_) #f]
    [(_ e1 e2 ...)
     (let ([t e1])
       (if t t (or e2 ...)))]))

И правильное определение:

(define-syntax or
  (syntax-rules ()
    [(_) #f]
    [(_ e) e]
    [(_ e1 e2 e3 ...)
     (let ([t e1])
       (if t t (or e2 e3 ...)))]))

Почему для правильного определения нужны три условия?Я запускаю много тестов, оба определения дают одинаковые результаты.Как подсказать, почему первое определение неверно?

Ответы [ 2 ]

0 голосов
/ 01 июня 2018

Давайте рассмотрим подсказку из книги.

Сначала мы определим нашу собственную версию 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 не находится в хвостовой позиции на выходе.

0 голосов
/ 01 июня 2018

Это тоже будет работать

(define-syntax foo
  (syntax-rules ()
  ((_) #f)
  ((_ e) e)
  ((_ e1 e2 ...)
   (let ((t e1))
     (if t t (foo e2 ...))))))
...