首页 > 解决方案 > Common Lisp 为符号添加后缀以在宏中使用

问题描述

您如何在宏中引入新的、可预测命名的带有后缀的标识符?


所以,我正在编写一个用于操作四元数的简单库。我暂时使用可能可行的最简单的表示形式,即组件列表,但我想定义一个不依赖于该表示形式的简单 API。

在定义算法时,我想用一个可预测的名称来引用四元数的每个组件,例如somesymbol-realpartor somesymbol-i

我希望能够使以下代码段正常工作。

(let
     ((my-quat '(1 2 3 4)))
  (with-quaternion my-quat
           (assert-equalp 1 my-quat-realpart)
           (assert-equalp 2 my-quat-i)
           (assert-equalp 3 my-quat-j)
           (assert-equalp 4 my-quat-k)))

但是,我用于生成带有后缀的符号的方法似乎会生成带有转义大写字符的奇怪的区分大小写的符号。

(defun add-suffix-to-symbol (sym suffix)
  (intern (concatenate 'string "" (string sym) "-" suffix)))

作为将符号转换为字符串的结果,它以大写形式打印......这是一个完全有效的规范化。但是,由于某种原因,通过创建新符号intern保留了大小写,因此我必须执行以下操作来引用with-quaternion.

(let
    ((my-quat '(1 2 3 4)))
  (with-quaternion my-quat
           (assert-equalp 1 |MY-QUAT-realpart|)
           (assert-equalp 2 |MY-QUAT-i|)
           (assert-equalp 3 |MY-QUAT-j|)
           (assert-equalp 4 |MY-QUAT-k|)))

如何创建一个与旧符号相同但带有后缀的新符号,以便可以在宏中使用?

供参考,这是所有代码。

(defun assert-equalp (e a)
  (assert (equalp e a)))

(defun quat-realpart (q)
  (first q))

(defun quat-i (q)
  (second q))

(defun quat-j (q)
  (third q))

(defun quat-k (q)
  (fourth q))

(assert-equalp '1 (quat-realpart '(1 2 3 4)))
(assert-equalp '2 (quat-i '(1 2 3 4)))
(assert-equalp '3 (quat-j '(1 2 3 4)))
(assert-equalp '4 (quat-k '(1 2 3 4)))

(defun add-suffix-to-symbol (sym suffix)
  (intern (concatenate 'string "" (string sym) "-" suffix)))

(print (add-suffix-to-symbol 'a "suffix"))

(defgeneric with-quaternion-impl (q-sym body))

(defmethod with-quaternion-impl ((q-sym symbol) body)
  (let
      ((q-realpart (add-suffix-to-symbol q-sym "realpart"))
       (q-i (add-suffix-to-symbol q-sym "i"))
       (q-j (add-suffix-to-symbol q-sym "j"))
       (q-k (add-suffix-to-symbol q-sym "k")))
    `(let
     ((,q-realpart (quat-realpart ,q-sym))
      (,q-i (quat-i ,q-sym))
      (,q-j (quat-j ,q-sym))
      (,q-k (quat-k ,q-sym)))
       (progn ,@body))))


(defmacro with-quaternion (q-sym &rest body)
  (with-quaternion-impl q-sym body))


(let
    ((my-quat '(1 2 3 4)))
  (with-quaternion my-quat
           (assert-equalp 1 |MY-QUAT-realpart|)
           (assert-equalp 2 |MY-QUAT-i|)
           (assert-equalp 3 |MY-QUAT-j|)
           (assert-equalp 4 |MY-QUAT-k|)))

(let
    ((my-quat '(1 2 3 4)))
  (with-quaternion my-quat
           (assert-equalp 1 my-quat-realpart)
           (assert-equalp 2 my-quat-i)
           (assert-equalp 3 my-quat-j)
           (assert-equalp 4 my-quat-k)))

在 下运行时clisp,它会打印以下符号,清楚地带有转义的大写字符。

|A-suffix|

并产生以下错误消息:

*** - PROGN: variable MY-QUAT-REALPART has no value

标签: common-lisp

解决方案


common lisp 中的符号默认为大写。明显的不区分大小写是因为您键入的所有内容在读取/检索时都会转换为大写,除非您使用带有条形字符的特殊语法|My-case-sensitive-SYMBOL|my-case-insensitive-symbolMY-CASE-INSENSITIVE-SYMBOL引用相同的实习符号,该符号以全部大写形式存储(尽管这是常见的 lisp,通常可以使用命令行选项和阅读器宏来更改它)。该符号实际上根本不区分大小写,它只是这样显示,因为您的代码中的大多数符号都是由阅读器大写的,除非您通过将它们包围在条形字符中或故意用不寻常的阅读器配置环境来特别免除它们选项。

以上所有内容的最终效果是,如果您想使用更熟悉的语法访问宏生成的符号,请确保在它被实习之前将所有组件都大写,例如:

(add-suffix-to-symbol q-sym "I")

代替

(add-suffix-to-symbol q-sym "i")

另一种选择是传递要连接的符号而不是字符串,例如

(defun add-suffix-to-symbol (sym suffix)
  (intern (concatenate 'string "" (string sym) "-" (string suffix))))

(print (add-suffix-to-symbol 'FOO 'bar)) ; foo-bar
(print (add-suffix-to-symbol 'foo '|bar|)) ; |FOO-bar| because foo is converted to FOO at read time

推荐阅读