common-lisp - Common Lisp 为符号添加后缀以在宏中使用
问题描述
您如何在宏中引入新的、可预测命名的带有后缀的标识符?
所以,我正在编写一个用于操作四元数的简单库。我暂时使用可能可行的最简单的表示形式,即组件列表,但我想定义一个不依赖于该表示形式的简单 API。
在定义算法时,我想用一个可预测的名称来引用四元数的每个组件,例如somesymbol-realpart
or 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 中的符号默认为大写。明显的不区分大小写是因为您键入的所有内容在读取/检索时都会转换为大写,除非您使用带有条形字符的特殊语法|My-case-sensitive-SYMBOL|
。my-case-insensitive-symbol
并MY-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
推荐阅读
- npm - 如何防止 NPM 使用带有版本的 git url 覆盖依赖项
- python - 如何使用 while 循环摆脱空字符串?(Python)
- grep - 使用 Bash 从 Cargo.toml 中提取 bin 名称
- flutter - 扫描条形码后,如何将扫描的数据传递到新页面?
- google-apps-script - 遍历 Gmail 附件并下载到 Google Drive 的最有效方法是什么?
- java - 每当我尝试读取文件时,我都会收到错误消息,我尝试拖动 txt 文件,但仍然出现错误。我想知道是不是有什么问题?
- python - Asyncio 递归限制和最大线程处理
- node.js - Gmail NodeJs客户端中的Gmail API userId用例?
- c# - SDK风格项目文件如何优化自动生成版本号
- c++ - 在 DLL 注入中访问内存会导致内存访问冲突