Common Lisp为符号添加后缀以在宏中使用

Gre*_*bet 1 common-lisp

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


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

在定义算法时,我想用可预测的名称(如somesymbol-realpart或)来引用四元数的每个组件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)))
Run Code Online (Sandbox Code Playgroud)

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

(defun add-suffix-to-symbol (sym suffix)
  (intern (concatenate 'string "" (string sym) "-" suffix)))
Run Code Online (Sandbox Code Playgroud)

作为将符号转换为字符串的结果,它以大写字母打印...这是一个完全有效的规范化.但是,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|)))
Run Code Online (Sandbox Code Playgroud)

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

供参考,这里是所有代码.

(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)))
Run Code Online (Sandbox Code Playgroud)

在下面运行时clisp,它会打印以下符号,清楚地显示转义大写字符.

|A-suffix|
Run Code Online (Sandbox Code Playgroud)

并生成以下错误消息:

*** - PROGN: variable MY-QUAT-REALPART has no value
Run Code Online (Sandbox Code Playgroud)

Joe*_*lor 6

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

以上所有内容的净效果是,如果您想使用更熟悉的语法访问宏生成的符号,请确保在实例化之前将所有组件设置为大写,例如:

(add-suffix-to-symbol q-sym "I")
Run Code Online (Sandbox Code Playgroud)

代替

(add-suffix-to-symbol q-sym "i")
Run Code Online (Sandbox Code Playgroud)

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

(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
Run Code Online (Sandbox Code Playgroud)