Гигиенический макрос r7rs: вернуть значение второго выражения

В настоящее время я изучаю некоторые r7rs и пытаюсь реализовать макрос 'begin' следующим образом:

(begin0 expr0 expr1 ... expr2)

С expr, являющимся регулярным выражением (Like (set! X (+ x 1)))

И begin0 как макрос, который вычисляет все выражения, но возвращает только результат expr1.

Например:

(let ((year 2017))
(begin1 (set! year (+ year 1))
  year
  (set! year (+ year 1))
  year)) 

Это должно вернуть 2018

Сначала я создал функцию начала:

(define-syntax begin0
 (syntax-rules ()
  ((begin-0 body-expr-0 body-expr-1 ...)
   (let ((tmp body-expr-0)) body-expr-1 ... tmp))))

И теперь я пытаюсь понять, как я могу сделать, чтобы вернуть значение "body-expr-1"? Я сделал следующий код, но он говорит, что мне не хватает многоточия, и я не понимаю, как это сделать.

(define-syntax begin1
  (syntax-rules ()
    ((begin1 body-expr-0 body-expr-1 ... body-expr-2)
     (let ((tmp body-expr-0) body-expr-1 ... tmp)
       (cond (eq? tmp body-expr-1)
              (begin . tmp))))))

Я надеюсь, что это достаточно понятно, спасибо за ответы.

2 ответа

Решение

Это может быть сделано, но макрос будет мешать так, что вы не можете делать все с begin1 как с begin,

(define-syntax begin1
   (syntax-rules ()
     ((_ expr0 expr1 exprn ...)
      (begin
        expr0
        (let ((result expr1))
          exprn ...
          result)))))

Код, который не работает, это:

(begin1
  (define global1 10)
  test3
  (define global2 20))

Причина очевидна. Расширяется до:

(begin1
  (define global1 10)
  (let ((result~1 test3))
    (define global2 20)
    result~1))

Второй define будет изменен на letrec такой, что переменная global2 доступно только на время let, У меня нет решения для этого, так как это требует от вас, чтобы иметь возможность делать глобальные define от закрытия.

begin1 довольно странная особенность. В Ракетке и, возможно, на других диалектах Схемы мы имеем begin0 который возвращает результат первого выражения. Это очень полезно. например. вот счетчик:

(define (get-counter from)
  (lambda ()
    (let ((tmp from))
      (set! from (+ from 1))
      tmp)))

И с begin0:

(define (get-counter from)
  (lambda ()
    (begin0 
      from
      (set! from (+ from 1)))))

В ракетке begin0 это примитив. Так что это форма, поддерживаемая в полностью расширенной программе и, таким образом, реализованная в C, так же, как begin..

Итак, я нашел возможный способ сделать это, но не смог, хотя мы могли бы просто спросить условие о немедленном значении:

(define-syntax begin1
  (syntax-rules ()
    ((begin1 body-expr-0 body-expr-1 body-expr-2 ...)
       (if body-expr-1
          (write body-expr-1)))))
Другие вопросы по тегам