Схема / "Закаленный интриган": Вопрос о синтаксисе определения функции "try" - PullRequest
1 голос
/ 19 февраля 2020

В своей книге «Закаленный интриган» Феллайзен и Фридман вводят функцию try. Согласно http://community.schemewiki.org/?seasoned-schemer эта функция может быть определена как

(define-syntax try
  (syntax-rules ()
    ((try var a . b)
     (letcc success 
       (letcc var (success a)) . b))))

, где letcc определяется как

(define-syntax letcc 
  (syntax-rules () 
    ((letcc var body ...) 
     (call-with-current-continuation 
       (lambda (var)  body ... ))))) 

Теперь, пока я понимаю, что try делает и как это можно использовать, у меня возникли проблемы, чтобы следовать формальному определению этого. Что именно означает точка в приложении от letcc к success и (letcc var (success a)) . b в строках

(letcc success 
       (letcc var (success a)) . b)

из try? Или может быть задан другой вопрос: Какая часть определения try устанавливает, что try оценивается как b, если var вызывается в a?

Редактировать 1: Извините, определение letcc было неполным. Добавлена ​​пропущенная первая строка.

Edit 2: Следующий код можно запустить в Racket.

(define-syntax letcc
   (syntax-rules ()
                 ((letcc var body ...)
                  (call-with-current-continuation
                    (lambda (var)  body ... )))))

 (define-syntax try
   (syntax-rules ()
                 ((try var a . b)
                  (letcc success
                         (letcc var (success a)) . b))))

(try var (+ 1 1) 4)
; output: 2

(try var (var '(2)) 4)
; output: 4

Ответы [ 3 ]

1 голос
/ 19 февраля 2020

Я не эксперт по тонкостям синтаксиса макросов Scheme, но я думаю эквивалентное определение try:

(define-syntax try 
  (syntax-rules () 
    ((try var a b ...) 
     (letcc success 
       (letcc var (success a)) b ...))))

Мне гораздо проще прочитайте наверняка.

(try e <a> <b> <c>) (любая версия, по крайней мере, в Racket) затем расширяется до

(letcc success
  (letcc e
    (success <a>))
  <b> <c>)))

Итак, тогда, когда <a> оценивается, e продолжение, которое возвращает свои аргументы из внутренней letcc формы, где они игнорируются. Если вызывается e, то на этом вы заканчиваете, а затем <b> и <c> оцениваются обычным способом (я поместил туда только одну вещь, потому что могу, и это касается всего . ... вещь). если e не вызывается во время оценки <a>, тогда вызывается success , и это также продолжение, которое затем возвращает результат оценки <a> из всей формы.

По крайней мере, я думаю, что так и происходит.


Ниже приведен фрагмент Ракетки, который я использовал для проверки, я понял вещи.

(module+ test
  (require rackunit))

(define-syntax let/cc 
  (syntax-rules () 
    ((letcc var body ...) 
     (call-with-current-continuation 
       (lambda (var) body ... ))))) 

(define-syntax try 
  (syntax-rules () 
    ((try var a b ...) 
     (let/cc success 
       (let/cc var (success a)) b ...))))

(module+ test
  (check-eqv?
   (try fail (+ 1 1) 4)
   2)

  (check-eqv?
   (try fail (fail '(2)) 4)
   4)

  (check-eqv?
   (try fail
        (begin 1 (fail) (error "failed to fail"))
        4 5 6)
   6))
1 голос
/ 20 февраля 2020

Давайте попробуем и посмотрим, что произойдет. Я использую мит-схему.

Файл try.scm:

(define-syntax letcc
   (syntax-rules ()
                 ((letcc var body ...)
                  (call-with-current-continuation
                    (lambda (var)  body ... )))))

 (define-syntax try
   (syntax-rules ()
                 ((try var a . b)
                  (letcc success
                         (letcc var (success a)) . b))))

(try var (+ 1 1) 4)

(try var (var '(2)) 4)

Первый шаг: вы компилируете файл:

(sf "try")

Это сгенерирует try.bin.

Второй шаг, напечатайте синтаксис desugared:

(pp (unsyntax (fasload "try") ))

;Loading "try.bin"... done
 ................
 (call-with-current-continuation
  (lambda (success)
    (call-with-current-continuation (lambda (var) (success (+ 1 1))))
    4))
 (call-with-current-continuation
  (lambda (success)
    (call-with-current-continuation (lambda (var) (success (var '(2)))))
    4)))

Теперь вы ясно видите, что выполняется, отсюда и результат.

В случае (try var (+ 1 1) 4) вы выпрыгиваете 2 вложенных calcc, так как вы вызовите success со значением 2, в то время как в (try var (var '(2)) 4) вы выпрыгнете на 1 уровень и будет возвращен 4 из последовательности 1-го продолжения.

1 голос
/ 19 февраля 2020

Синтаксические правила - это сопоставление с образцом. Точка указывает car и cdr пары, как и остальные аргументы в lambda / define:

(define (my-list . args)
  args)

Список - это просто вложенные пары. например. (1 2 3) - это просто причудливый способ отображения (1 . (2 . (3 . ()))).

Так что (this is random symbols in a list) будет соответствовать (try var a . b) на try совпадение this, is совпадение var, random совпадение a и (symbols in a list) соответствует b.

Когда вы видите то же самое в расширении, это означает, что код должен соединить совпадение после точки. Например, (var . b) с предыдущим примером становится (is symbols in a list). Это похоже на b ..., но дешевле для системы.

...