Этюд актуален для любого языка с аппликативным порядком вычисления и макросами, однако в силу происхождения этюда — я предлагаю его в контексте scheme.
В scheme "or" — short-circuit операция, которая принимает произвольное число аргументов. Реализовать "or" можно в виде макроса.
Вот вариант реализация "or":
(define-syntax or
(syntax-rules ()
((_) #f)
((_ e1 e2 ...)
(let ((t e1))
(if t t (or e2 ...))))))
Этот вариант "or" будем считать "неправильным", т.к. он обладает определенным недостатком (с приходом r6rs можно сказать, что двумя).
Вот такой вариант "or" (в нем появляется дополнительное условие) будем считать "правильным":
(define-syntax or
(syntax-rules ()
((_) #f)
((_ e) e)
((_ e1 e2 e3 ...)
(let ((t e1))
(if t t (or e2 e3 ...))))))
В связи с этим, предлагаю ответить на два вопроса.
— Каким недостатком обладает "неправильный" по сравнению с правильным?
— Приведите пример кода, который будет по разному вести себя для "неправильной" и "правильной" реализации "or".
PS: Все-же этюд касается не только scheme, так что, если надо, могу немного рассказать о семантике вышеозначенных макросов или попытаться накидать аналогичные макросы, скажем, на nemerle.
Здравствуйте, Mr.Cat, Вы писали:
MC>PS: Все-же этюд касается не только scheme, так что, если надо, могу немного рассказать о семантике вышеозначенных макросов или попытаться накидать аналогичные макросы, скажем, на nemerle.
А можно на Common Lisp? Я вообще не понимаю схемовских макросов. =)
Здравствуйте, jartur, Вы писали: J>А можно на Common Lisp? Я вообще не понимаю схемовских макросов. =)
Охх, попробую. Случаем, нет у тебя годной ссылки на "quickstart" по макросам и паттерн-матчингу (syntax-rules — суть паттерн-матчинг для s-выражений) в cl?
MC>Охх, попробую. Случаем, нет у тебя годной ссылки на "quickstart" по макросам и паттерн-матчингу (syntax-rules — суть паттерн-матчинг для s-выражений) в cl?
А в общих чертах, там все тупо:
(defmacro name (params*)
body*)
Впрочем по сути макрос — просто функция, которая возвращает форму. При этом ` (backquote) используется для экранирования вичисления, а , и ,@ для их форсирования. Ну например
(defmacro if1 (test true-branch &optional else-branch)
"My super if."
`(cond (,test ,true-branch)
(t ,else-branch)))
Тогда
(if1 (= a b)
(+ a b)
(- a b))
Развернется в (COND ((= A B) (+ A B)) (T (- A B))).
При этом возможен захват переменных и прочие неприятности, потому для имен переменных локальных для макроса нужно использовать gensym. А паттерны можно задавать в строке params. Например
(defmacro for ((a b) &body body)
(let ((a-var (gensym)))
`(do ((,a-var ,a (+ 1 ,a-var)))
((>= ,a-var ,b))
,@body)))
Тогда
(for (1 5)
(+ 1 1)
(+ 2 2))
превращается в (DO ((#:G826 1 (+ 1 #:G826))) ((>= #:G826 5)) (+ 1 1) (+ 2 2))
Если бы мы не использовали gensym, и вместо #:G826 стояло бы SOMEVAR, то в случае наличия SOMEVAR в lexical scope во время вызова макроса, мы бы поимели неприятный эффект. Ладно shadowing, а если бы мы деструктивно присваивали бы что-то SOMEBVAR внутри макроса? Ну понятно, в общем. Никакой гигиены =)
Ну да ладно, лучше в книжке прочитать, там подробнее и понятнее.
Здравствуйте, jartur, Вы писали: J>Вообще можно здесь прочитать: http://www.gigamonkeys.com/book/macros-defining-your-own.html
Ммм, точно. Как-то я позабыл про practical common lisp (как-то даже начинал читать, да бросил). Что ж, попробую переписать в cl-макросы...
Re: [Этюд]Макрос "or"
От:
Аноним
Дата:
29.04.09 10:48
Оценка:
Можно подсказку? А то ничего умнее
(define-syntax or
(syntax-rules ()
((_) #f)
((_ e1 e2 ...)
(let ((t e1))
(if t t (or e2 ...))))))
(define-syntax or1
(syntax-rules ()
((_) #f)
((_ e) e)
((_ e1 e2 e3 ...)
(let ((t e1))
(if t t (or1 e2 e3 ...))))))
(define (if x y z) (error ""))
(or false) ; ошибка
(or1 false) ; нет ошибки
Здравствуйте, Аноним, Вы писали: А>Я еще не отчаялся, так что лучше тонкую
Ок.
В принципе, недостаток у "неправильного" or довольно безобидный. Однако из-за этого он не удовлетворяет r6rs.
Re[5]: [Этюд]Макрос "or"
От:
Аноним
Дата:
30.04.09 10:22
Оценка:
Здравствуйте, Mr.Cat, Вы писали:
MC>Здравствуйте, Аноним, Вы писали: А>>Я еще не отчаялся, так что лучше тонкую MC>) Ок. MC>В принципе, недостаток у "неправильного" or довольно безобидный. Однако из-за этого он не удовлетворяет r6rs.
Ммм.. Scheme я знаю только в рамках SICP и непродолжительных ковыряний в доках по PLT Scheme. Так что для меня отличия r5rs и r6rs это rocket since
На первый взгляд, отличия самые минимальные, or1 ну разве что чуть громоздче выглядит. Оки-доки, залез в R6RS draft чтобы узнать, что же такого жуткого написано про "or" в R6RS:
(or <test> ...) Syntax: The <test>s must be expressions. Semantics: If there are no <test>s, #f is returned. Otherwise, the <test> expressions are evaluated from left to right until a <test> returns a true value val (see section 5.7) or the last <test> is reached. In the former case, the or expression returns val without evaluating the remaining expressions. In the latter case, the last expression is evaluated and its values are returned.
The or keyword could be defined in terms of if using syntax-rules (see section 11.19) as follows:
(define-syntax or
(syntax-rules ()
((or) #f)
((or test) test)
((or test1 test2 ...)
(let ((x test1))
(if x x (or test2 ...))))))
The last <test> expression is in tail context if the or expression itself is; see section 11.20.
Последняя фраза и наводит на ответ MC>- Каким недостатком обладает "неправильный" по сравнению с правильным?
Определение or1 не подчиняется требованию хвостовой рекурсии, а or2 — подчиняется.
MC>- Приведите пример кода, который будет по разному вести себя для "неправильной" и "правильной" реализации "or".
Достаточно залудить какие-нибудь длинные рекурсивные вычисления, версия без хвостовой рекурсии быстро сожрёт всю память.
MC>PS: Все-же этюд касается не только scheme, так что, если надо, могу немного рассказать о семантике вышеозначенных макросов или попытаться накидать аналогичные макросы, скажем, на nemerle.
гм... весьма специфичный этюд, однако.
Здравствуйте, Lazy Cjow Rhrr, Вы писали: MC>>- Каким недостатком обладает "неправильный" по сравнению с правильным? LCR>Определение or1 не подчиняется требованию хвостовой рекурсии, а or2 — подчиняется.
Именно. Если "правильный" or находится в хвостовой позиции — последний операнд тоже стоит в хвостовой позиции. "Неправильный" or такого не обеспечивает
Хотя здесь отметили, что имплементации могли бы оптимизировать "неправильный" or до состояния "правильного" (но вот, например, chicken не оптимизирует).
К>вернёт не булевское значение, вместо того, чтобы заругаться на месте.
Ага.
А вообще, в scheme любое значение, кроме #f, считается истинным с точки зрения if, or, and и т.п.
он не гигиеничный?
потому что в syntax-rules все никак руки не дойдут разобраться
C>Этюд актуален для любого языка с аппликативным порядком вычисления и макросами, однако в силу происхождения этюда — я предлагаю его в контексте scheme.
MC>В scheme "or" — short-circuit операция, которая принимает произвольное число аргументов. Реализовать "or" можно в виде макроса. MC>Вот вариант реализация "or": MC>
MC>(define-syntax or
MC> (syntax-rules ()
MC> ((_) #f)
MC> ((_ e1 e2 ...)
MC> (let ((t e1))
MC> (if t t (or e2 ...))))))
MC>
MC>Этот вариант "or" будем считать "неправильным", т.к. он обладает определенным недостатком (с приходом r6rs можно сказать, что двумя).
MC>Вот такой вариант "or" (в нем появляется дополнительное условие) будем считать "правильным": MC>
MC>В связи с этим, предлагаю ответить на два вопроса.
MC>- Каким недостатком обладает "неправильный" по сравнению с правильным? MC>- Приведите пример кода, который будет по разному вести себя для "неправильной" и "правильной" реализации "or".
MC>PS: Все-же этюд касается не только scheme, так что, если надо, могу немного рассказать о семантике вышеозначенных макросов или попытаться накидать аналогичные макросы, скажем, на nemerle.
Здравствуйте, GSergey, Вы писали: GS>Вот вариант для SCM Scheme GS>он не гигиеничный?
Гигиена — свойство самой макро-системы (syntax-rules, syntactic-closures и т.п.) а не конкретного макроса.
Re[2]: [Этюд]Макрос "or"
От:
Аноним
Дата:
11.05.09 04:21
Оценка:
GS>Здравствуйте, Mr.Cat, Вы писали:
GS>Вот вариант для SCM Scheme
GS>(defmacro or2 ( . args ) GS> (if (null? args) #f GS> `(if ,(car args) #t GS> (or2 ,@(cdr args)))))
GS>он не гигиеничный?
Во первых, да, он негигиеничный. Во вторых, он не является аналогом схемовского or, потому как последний возвращает первое не-#f значение, а Ваш or2 возвращает только #t или #f.
GS>потому что в syntax-rules все никак руки не дойдут разобраться
А вот как раз syntax-rules — простой до примитивизма. Там и разбираться то нечего — просто выучить очередной pattern-matching синтаксис (довольно интуитивно-понятный, к слову). syntax-case — гораздо мощнее, гибче и соответственно сложнее в нюансах.
Re[3]: [Этюд]Макрос "or"
От:
Аноним
Дата:
11.05.09 04:26
Оценка:
GS>>Вот вариант для SCM Scheme GS>>он не гигиеничный? MC>Гигиена — свойство самой макро-системы (syntax-rules, syntactic-closures и т.п.) а не конкретного макроса.
Автоматическая гигиена — свойство макросистемы, а гигиена — свойство самих макросов (результата их раскрытия)
hygiene condition for macro expansion:
“Generated identifiers that become binding instances in the completely expanded program must only bind variables that are generated at the same transcription step.”
Простой пример: автоматически гигеническая макросистема syntax-case позволяет нарушать гигиену и писать негигенические макросы.
Здравствуйте, Аноним, Вы писали: А>Автоматическая гигиена — свойство макросистемы, а гигиена — свойство самих макросов (результата их раскрытия)
Да, пожалуй так будет правильнее.