В чем разница между letre c -синтаксисом и let-синтаксисом в схеме? - PullRequest
0 голосов
/ 11 апреля 2020

Был похожий вопрос, но не с синтаксическими макросами, хотя разница в том, что один макрос не может видеть другой как letre c и let с функциями.

Но это работает так же с letre c -syntax и let-синтаксис

(let-syntax ((foo (lambda (x) `(bar (list ',(car x) ',(caadr x)))))
             (bar (lambda (x) `(display ',x))))
    (foo (list 1 2 3)))

Можете ли вы показать пример кода, в котором эти два макроса / синтаксиса отличаются?

EDIT:

Нашел следующее:

(let-syntax ((bar (lambda (x) `(display ',x)))
             (foo (lambda (x) (bar x))))
   (foo (list 1 2 3)))

, которые не работают с let-syntax и работают с letrec-syntax, но в чем выгода? Если эти локальные переменные не могут быть функциями (вывод (bar x)), какова цель let-syntax? Мне нужен пример, где он может быть использован в коде, где вам не нужен letrec-syntax, и было бы достаточно иметь let-syntax.

1 Ответ

3 голосов
/ 11 апреля 2020

Это то же самое, что и let и letrec. С let вы не можете ожидать наличия привязки при оценке значений. Это влияет только на создание процедуры / лямбда / замыкания, поскольку все процедуры вызываются в среде, в которой они созданы.

(define (test v)
  (list 'default v))

(let ((test (lambda (v) 
              (if (zero? v)
                  (list 0)
                  (cons v (test (- v 1)))))))
  (test 5))
; ==> (5 default 4)

(letrec ((test (lambda (v)
                (if (zero? v)
                    (list 0)
                    (cons v (test (- v 1)))))))
  (test 5))
; ==> (5 4 3 2 1 0)

Так что в примере let локальный let не находится в теле замыкание, потому что оно не существует, когда значения оцениваются. На практике, если вы расширите let до его эквивалентной лямбда-формы, вы поймете, почему он вызывает глобальный test:

((lambda (test) (test 5))
 (lambda (v)
   (if (zero? v)
       (list 0)
       (cons v (test (- v 1))))))

Вы понимаете, почему он не вызывает сам себя? letrec специально предназначен для создания локальных процедур, которые сами по себе являются обязательными для решения этой небольшой проблемы. Для правил синтаксиса он имеет те же свойства привязки для похожих имен, но мы имеем дело с синтаксисом, поэтому нам нужно, чтобы шаги не полагались на арифметику времени выполнения c:

(define-syntax stest
  (syntax-rules ()
    ((_ v . rest) '(default v))))

(let-syntax ((stest (syntax-rules () 
                      ((_ v . rest) (cons 'v (stest . rest)))
                      ((_) '()))))
  (stest 5 4 3 2 1 0))
; ==> (5 default 4)

(letrec-syntax ((stest (syntax-rules () 
                         ((_ v . rest) (cons 'v (stest . rest)))
                         ((_) '()))))
  (stest 5 4 3 2 1 0))
; ==> (5 4 3 2 1 0)

Снова letrec-syntax гарантирует, что stest доступен в среде трансформера syntax-rules, чтобы он соответствовал самому себе вместо макроса верхнего уровня.

Что касается ваших примеров, они не являются Схемой. Возможно, они будут работать в какой-то конкретной реализации схемы c в качестве дополнительной функциональности по умолчанию, но они не будут работать ни в одной реализации R5RS, R6RS или R7RS, как в моих примерах. R6RS имеет syntax-case в качестве дополнительного трансформатора, и я предполагаю, что R7RS-large также будет иметь дополнительные трансформаторы. Если вы придерживаетесь поведения defacto, вам нужно пометить реализацию c.

...