Arhn - архитектура программирования

Локальная переменная расширения макроса elisp

Недавно я коснулся elisp и попытался понять, как работает макрос elisp. Учебник GNU содержит глава Surprising-local-Vars для локальной переменной макроса, и я не понимаю, как работает расширение макроса.

(defmacro for (var from init to final do &rest body)
  "Execute a simple for loop: (for i from 1 to 10 do (print i))."
  (let ((tempvar (make-symbol "max")))
    `(let ((,var ,init)
           (,tempvar ,final))
       (while (<= ,var ,tempvar)
         ,@body
         (inc ,var)))))

Есть две формы let. первый

(let ((tempvar (make-symbol "max")))

не имеет обратной кавычки, которая будет оцениваться во фразе расширения макроса, поэтому неинтернированный символ «max» будет создан только в этой фразе. И неинтернированный символ "max" потеряется во время выполнения, он не должен работать правильно?

Но на самом деле работает хорошо. Я пробовал следующее:

(for max from 1 to 10 do (print max))

И его расширение следующим образом:

(macroexpand '(for max from 1 to 10 do (print max)))

(let ((max 1) (max 10)) (while (<= max max) (print max) (setq max (+ 1 max))))

Здесь два максимальных символа, один привязан к 1, другой привязан к 10, выражение формы while имеет два максимальных символа.

(while (<= max max)

как форма while разрешает два разных символа «max»?

08.07.2016

Ответы:


1

Вы смотрите на печатные имена, а не на идентификаторы символов. У вас есть два символа, оба с «печатным именем» max, но с разными идентификаторами. Обычно я бы рекомендовал использовать gensym, а не make-symbol, но на самом деле не имеет большого значения, как это делается.

Думайте о символе как об указателе на небольшую структуру, в которой хранится множество значений. Одним из них является «имя», когда символ интернирован, он помещается в специальную структуру, поэтому вы можете найти символ по его имени. Вы видите интернированный символ с именем max и неинтернированный символ с именем max. Это разные символы (то есть две структуры и указатели, таким образом, разные), но когда вы смотрите только на печатное представление, это не очевидно.

Быстрая демонстрация, только что из буфера emacs "scratch":

(defmacro demo (sym)
  (let ((tmp (make-symbol "max")))
    `(progn
       (message "%s" (list ',tmp ',sym))
       (eql ',tmp ',sym))))

demo

(macroexpand '(demo max))
(progn (message "%s" (list (quote max) (quote max))) (eql (quote max) (quote max)))

(demo max)
nil

Если вы вставите текст, полученный в результате раскрытия макроса, и оцените его, вы увидите, что получили t вместо nil, потому что во время чтения выражения вы получите тот же символ.

08.07.2016
  • имеет смысл. А по первому вопросу, не могли бы вы подробнее об этом рассказать? первая форма let не имеет обратной кавычки, поэтому она будет оцениваться при расширении фразы (в это время будет создан неинтернированный символ). И созданный неинтернированный символ, вероятно, не будет существовать во время выполнения. (подумайте о случай: скомпилируйте код elisp, макрос расширится, будет создан максимум неинтернированного символа. В следующий раз, когда я запущу его непосредственно из байт-кода, макс. неинтернированного символа не существует, как это работает?). 08.07.2016
  • Неинтернированный символ будет жить (как минимум) до тех пор, пока на него есть ссылки. В макрорасширенном коде есть ссылка на неинтернированный символ. Вы будете получать другой неинтернированный символ в расширении каждый раз, когда раскрывается макрос. Неинтернированный означает только то, что вы не можете найти символ по его имени, только имея ссылку на него. 08.07.2016
  • В Common Lisp установка для print-circle значения T обычно печатает символы с одинаковыми именами с переменными чтения. Кажется, в Emacs это не так. 08.07.2016

  • 2

    Короткий ответ заключается в том, что (make-gensym "max") создает новый символ с именем max. Вы также используете символ с именем max. Они имеют одно и то же имя, и поэтому в Emacs выводятся одинаково, но это разные символы. Мы можем легко проверить это, создав макрос, который создает символ и возвращает форму, которая сравнивает его с аргументом макроса:

    (defmacro test-gensym (arg)
      (let ((max (make-symbol "max")))
        `(eq ',max ',arg)))
    

    Если мы посмотрим на макрорасширение, то увидим, что мы сравниваем два символа с одинаковыми именами:

    (print (macroexpand '(test-gensym max)))
    ;;=> (eq (quote max) (quote max))
    

    Но если мы на самом деле запустим этот код, мы увидим, что сравниваемые значения не совпадают:

    (test-gensym max)
    ;;=> nil
    
    08.07.2016

    3

    Для сравнения с Common Lisp: здесь вы можете видеть, что принтер Common Lisp заставляет переменную выглядеть по-другому. Один — ваш max, а другой — неинтернированный #:max.

    CL-USER 70 > (pprint (macroexpand '(for max from 1 to 10 do (print max))))
    
    (LET ((MAX 1) (#:MAX 10))
      (LOOP WHILE (<= MAX #:MAX) DO (PROGN (PRINT MAX) (INCF MAX))))
    

    Если мы скажем принтеру Common Lisp поддерживать печать циклических структур данных, то принтер пометит, где неинтернированные символы совпадают: #1=#:MAX — это символ с меткой принтера. #1# тогда является ссылкой на эту помеченную вещь.

    CL-USER 71 > (setf *print-circle* t)
    T
    
    CL-USER 72 > (pprint (macroexpand '(for max from 1 to 10 do (print max))))
    
    (LET ((MAX 1) (#1=#:MAX 10))
      (LOOP WHILE (<= MAX #1#) DO (PROGN (PRINT MAX) (INCF MAX))))
    

    Но это Common Lisp, а не Emacs Lisp, хотя они связаны между собой тем, что оба являются преемниками более раннего диалекта Lisp.

    08.07.2016
    Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..