Макроси на схема - Сдвояване в трансформацията, но списък като изход?

Да кажем, че имам следния макрос в R5RS Scheme:

(define-syntax pair-test
  (syntax-rules ()
     ((_ (a b . c))
      (quote (a b . c)))))

Макросът трансформира входна двойка в изходна двойка, както може да се очаква:

(pair-test (1 2 . 3))
==> (1 2 . 3)

Мога също да предам списък на макроса, както е позволено от спецификацията. Резултатът обаче е списък вместо двойка:

(pair-test (1 2 3))
==> (1 2 3)

Какво точно се случва тук? Защо изходът е списък вместо двойка?


person Justin Ethier    schedule 28.08.2011    source източник


Отговори (2)


Може ли c да бъде (3 . ()) във втория случай? Не съм положителен, но това би имало смисъл за мен. И тогава цитирането на (a b . c) ще бъде (1 2 . (3 . ())), което е (1 2 . (3)) и (3) е правилен списък, така че (1 2 3)?

person Ross Larson    schedule 28.08.2011
comment
да Всъщност c е (3 . ())---допълнителната кавичка, която имате, не трябва да е там---но по същество сте прав. - person Ryan Culpepper; 28.08.2011

За да видите какво се случва тук, трябва да знаете, че списъкът в Scheme е рекурсивна верига от двойки елементи и други списъци. Всички данни, които следват формата на списък, винаги ще бъдат отпечатани като списък. След като разберете как са изградени основните списъци, ще можете да видите какво се случва във вашия макрос.

Двойките в Scheme могат да бъдат създадени с помощта на оператора . или с помощта на функцията cons. Ето една проста двойка числа:

(quote (1 . 2))
==> '(1 . 2)
(cons 1 2)
==> '(1 . 2)

За да създадете списък от 1 в Scheme, можете да направите двойка от нещо и празния списък:

(quote (1 . ()))
==> '(1)
(cons 1 (list))
==> '(1)

Списък от 2 е двойка нещо или нещо от лявата страна, а списък от 1 от дясната страна. По същия начин списък от 3 е един елемент, съчетан със списък от 2:

(quote (1 . (2 . (3 . ()))))
==> '(1 2 3)
(cons 1 (cons 2 (cons 3 (list))))
==> '(1 2 3)

За да видите какво прави вашия макрос, можете да пренаредите (quote (a b . c)), за да бъде по-ясно:

(quote (a . (b . c)))
(cons (quote a) (cons (quote b) (quote c)))

Сега можете да видите, че този формуляр изглежда много подобно на този, когато съставяте списък. Ако (quote c) води до списък, тогава целият израз ще бъде списък. В случай на (pair-test (1 2 3)), c става (3 . ()):

(quote (a . (b . c)))
==> (quote (1 . (2 . (3 . ()))))
==> '(1 2 3)
(cons (quote a) (cons (quote b) (quote c)))
==> (cons '1 (cons '2 '(3 . ())))
==> '(1 2 3)

Тази стойност се отпечатва от REPL като списък, защото е "правилен списък". Всяка дясна страна (cdr) е списък, чак до празния списък в края, така че тази стойност перфектно следва формата на списък. REPL предполага, че искате да видите резултата като списък, така че той се отпечатва без ..

Ще видите '(1 2 . 3) за (pair-test (1 2 . 3)), защото по този начин REPL отпечатва "неправилни списъци". Ако последният елемент във веригата от двойки не е празният списък, стойността се счита за "неправилен списък" и ще бъде отпечатана по различен начин:

(quote (1 . (2 . 3)))
==> '(1 2 . 3)
(cons 1 (cons 2 3))
==> '(1 2 . 3)
person Dan Cecile    schedule 28.08.2011