Common lisp: как макрос может определять другие методы/макросы с программно сгенерированными именами?

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

Пример (неправильный код)

(defmacro def-trio (base-name)
  (let 
    ((helper-name (format nil "helper-~a" base-name))
     (method-1 (format nil "~a-1" base-name))
     (method-2 (format nil "~a-2" base-name)))
    `(progn 
          (defun ,helper-name () 'helper-called)
          (defun ,method-1 () (,helper-name) '1-called)
          (defun ,method-2 () (,helper-name) '2-called))))

Теперь происходит следующее:

(def-trio my-trio)

== >

(PROGN (DEFUN "helper-MY-TRIO" () 'HELPER-CALLED)
       (DEFUN "MY-TRIO-1" () ("helper-MY-TRIO") '1-CALLED)
       (DEFUN "MY-TRIO-2" () ("helper-MY-TRIO") '2-CALLED))

Кроме того, после того, как я узнаю, как это сделать, есть ли какие-либо дополнительные ключи, если бы этот макрос определял другие макросы вместо других функций? Я прочитал Как написать макроопределяющий макрос в общем lisp, но я думаю, что мой вопрос немного отличается, потому что я спрашиваю о программно сгенерированных символах/именах, Я открыт для исправления, хотя:) Спасибо!

Ответ 1

Попробуйте следующее:


(defmacro def-trio (base-name)                                         ; changes:
  (let*                                                                ; 3.
    ((package (symbol-package base-name))                              ; 2.
     (helper-name (intern (format nil "HELPER-~a" base-name) package)) ; 1. 4.
     (method-1 (intern (format nil "~a-1" base-name) package))         ; 1.
     (method-2 (intern (format nil "~a-2" base-name) package)) )       ; 1.
    `(progn 
          (defun ,helper-name () 'helper-called)
          (defun ,method-1 () (,helper-name) '1-called)
          (defun ,method-2 () (,helper-name) '2-called) )))

В исходное определение были внесены следующие изменения: первое изменение имеет решающее значение:

  • Интерполяция каждого из вычисленных имен символов в тот же пакет, что и базовое имя, с помощью (intern ... package).
  • Введена переменная package, привязанная к пакету поставляемого символа base-name.
  • Изменен let на let*, чтобы разрешить новую введенную переменную package ссылаться на следующие переменные.
  • Изменен префикс метода помощника в верхнем регистре, чтобы он соответствовал стандарту для обычных символов Lisp.

Ответ 2

Используйте INTERN, чтобы превратить строки сгенерированных имен функций в символы.