Кто-то может объяснить понятие 'гигиены' мне (я - программист схемы)?

Так... Я плохо знаком со схемой r6rs и изучаю макросы. Кто-то может объяснить мне, что предназначено 'гигиеной'?

Заранее спасибо.

28
задан Cam 27 June 2010 в 23:21
поделиться

6 ответов

Гигиена часто используется в контексте макросов. Гигиеничный макрос не использует имена переменных, которые могут помешать расширяемому коду. Вот пример. Допустим, мы хотим определить специальную форму или с помощью макроса. Интуитивно

(или a b c ... d) будет расширяться до чего-то вроде (let ((tmp a)) (if tmp a (or b c ... d))) . (Я опускаю пустой случай (или) для простоты.)

Теперь, если бы имя tmp было действительно добавлено в код, как в приведенном выше расширении, оно бы быть не гигиеничным и плохим, потому что это может помешать другой переменной с тем же именем. Скажем, мы хотели оценить

(let ((tmp 1)) (or #f tmp))

. Используя наше интуитивное расширение, это будет

(let ((tmp 1)) (let ((tmp #f)) (if tmp (or tmp)))

tmp из макроса, затеняющего самый внешний tmp , и поэтому результат будет #f вместо 1 .

Теперь, если макрос был гигиеничным (а в Scheme это автоматически происходит при использовании syntax-rules ), то вместо использования имени tmp для расширения вы будет использовать символ, который гарантированно не появится нигде в коде. Вы можете использовать gensym в Common Lisp.

В книге Пола Грэма «О Лиспе» есть расширенный материал по макросам.

24
ответ дан 28 November 2019 в 03:30
поделиться

Если вы представите, что макрос просто разворачивается в том месте, где он используется, то вы также можете представить, что если вы используете переменную a в своем макросе, там уже может быть переменная a , определенная в том месте, где используется этот макрос.

Это не тот , который вам нужен!

Макросистема, в которой чего-то подобного не может произойти, называется гигиенической .

Есть несколько способов решения этой проблемы. Один из способов - просто использовать в макросах очень длинные, очень загадочные и очень непредсказуемые имена переменных.

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

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

8
ответ дан 28 November 2019 в 03:30
поделиться

Я так рада узнать, что этот язык все еще используется! Гигиенический код - это код, который при введении (через макрос) не вызывает конфликтов с существующими переменными.

В Википедии есть много полезной информации об этом: http://en.wikipedia.org/wiki/Hygienic_macro

3
ответ дан 28 November 2019 в 03:30
поделиться

Вот что я нашел. Разъяснять, что это значит - совсем другое дело!

http://www.r6rs.org/final/html/r6rs-lib/r6rs-lib-ZH-1.html#node_toc_node_sec_12.1

2
ответ дан 28 November 2019 в 03:30
поделиться

Макросы преобразуют код: они берут один бит кода и преобразуют его во что-то другое. В процессе преобразования они могут окружать этот код дополнительным кодом. Если исходный код ссылается на переменную a, а код, добавленный вокруг нее, определяет новую версию a, то исходный код не будет работать так, как ожидалось, потому что он будет обращаться к неправильной a: if

(myfunc a)

- исходный код, который ожидает, что a будет целым числом, а макрос берет X и преобразует его в

(let ((a nil)) X)

Тогда макрос будет работать нормально для

(myfunc b)

но (myfunc a) будет преобразован в

(let ((a nil)) (myfunc a))

который не будет работать, потому что myfunc будет применен к nil, а не к целому числу, которое он ожидает.

Гигиенический макрос позволяет избежать этой проблемы обращения к неправильной переменной (и аналогичной проблемы в обратном направлении), обеспечивая уникальность используемых имен.

В Википедии есть хорошее объяснение гигиенических макросов.

2
ответ дан 28 November 2019 в 03:30
поделиться

Помимо всего упомянутого, в гигиенических макросах Scheme есть еще одна важная вещь, которая вытекает из лексической области видимости.

Допустим, у нас есть:

(syntax-rules () ((_ a b) (+ a b)))

Как часть макроса, он обязательно вставит +, он также вставит его, когда там уже есть +, но затем другой символ, который имеет то же значение, что и + . Он привязывает символы к значению, которое они имели в лексической среде, в которой находятся syntax-rules , а не к тому месту, где они применяются, в конце концов, мы находимся в лексической области видимости. Скорее всего, он вставит туда совершенно новый символ, но тот, который глобально привязан к тому же значению, что и + , находится в том месте, где определен макрос. Это наиболее удобно, когда мы используем такую ​​конструкцию, как:

(let ((+ *))
  ; piece of code that is transformed
)

Таким образом, автор или пользователь макроса не должен заботиться о том, чтобы его использование прошло успешно.

2
ответ дан 28 November 2019 в 03:30
поделиться
Другие вопросы по тегам:

Похожие вопросы: