反引用 (backquote) 是引用(quote) 的特别版本,它可以用来创建 Lisp 表达式的模板。反引用最常见的用途之一是用在宏定义里。
反引用字符 “‘” 得名的原因是:它和通常的引号 “’” 相似,只不过方向相反。当单独把反引用作为表达式前
缀的时候,它的行为和引号一样:
‘(a b c) 等价于’(a b c) .
只有在反引用和逗号 “,” ,以及comma-at “,@ ” 一同出现时才变得有用。如果说反引用创建了一个模板,那
么逗号就在反引用中创建了一个 slot。一个反引用列表等价于将其元素引用起来,调用一次list。也就
是,
‘(a b c) 等价于(list ’a ’b ’c) .
在反引用的作用域里,逗号要求 Lisp:“把引用关掉”。当逗号出现在列表元素前面时,它的效果就相当于取
消引用,让 Lisp 把那个元素按原样放在那里。所以
‘(a ,b c ,d) 等价于(list ’a b ’c d) .
插入到结果列表里的不再是符号b,取而代之的是它的值。无论逗号在嵌套列表里的层次有多深,它都仍
然有效,
> (setq a 1 b 2 c 3)
3>
‘(a ,b c)
(A 2 C)
> ‘(a (,b c))
(A (2 C))
而且它们也可以出现在引用的列表里,或者引用的子列表里:
> ‘(a b ,c (’,(+ a b c)) (+ a b) ’c ’((,a ’b)))
(A B 3 (’6) (+ A B) ’C ’((1 ’B)))
一个逗号能抵消一个反引用的效果,所以逗号在数量上必须和反引用匹配。如果某个操作符出现在逗号的
外层,或者出现在包含逗号的那个表达式的外层,那么我们说该操作符包围了这个逗号。例如在‘(,a ,(b
‘,c)) 中,最后一个逗号就被前一个逗号和两个反引号所包围。通行的规则是:一个被 n 个逗号包围的逗
号必须被至少 n + 1 个反引号所包围。很明显,由此可知:逗号不能出现在反引用的表达式的外面。只要
遵守上述规则,就可以嵌套使用反引用和逗号。下面的任何一个表达式如果输入到 toplevel 下都将造成错
误:
,x ‘(a ,,b c) ‘(a ,(b ,c) d) ‘(,,‘a)
嵌套的反引用是Lisp宏的难点,正如《On Lisp》所说的:
“为了定义一个定义宏的宏,我们通常会要用到嵌套的反引用。嵌套反引用的难以理解是出了名的。尽管最
终我们会对那些常见的情况了如指掌,但你不能指望随便挑一个反引用表达式,都能看一眼,就能立即说出
它可以产生什么。这不能归罪于Lisp。就像一个复杂的积分,没人能看一眼就得出积分的结果,但是我们
不能因为这个就把问题归咎于积分的表示方法。道理是一样的。难点在于问题本身,而非表示问题的方法。”
stackoverflow有一个问答对Lisp的嵌套反引用解释的很透彻,我们下面来分析一下:
先来说一下嵌套反引用的解析规则:
CLTS2里面说:
如果反引号是嵌套的话,最内层的反引用形式(这是说的是最内层的逗号,其实是最外层的反引号)应该最先被展开。这意味着如果某个表达式有几个连续的逗号的话,最左边的那个逗号属于最里面的反引号。
R5RS Scheme语言规范关于反引号这么定义:
准引用可以嵌套。
举例分析:
第一次求值得到如下表达式:
解析:1,左边反引用首先被展开(第一个反引用),所以(+ 1 2)被求值因为匹配逗号(第二个逗号)。
2,另一个表达式,(+ 3 4)因为没有足够的逗号所以未求值。
3,只有一个反引用被展开,因为反引用不会递归的展开。
第二次求值(展开所有的逗号)
为了在展开所有的反引用,我们采用如下表达式:
(eval ``(a ,,(+ 1 2) ,(+ 3 4)))
所用的反引用被展开,我们带到下面的求值结果:
(A 3 7)
解析: 其实就是对第一次求值的结果继续求值,就可以得到上面的结果。
<<On Lisp>>一书上说,反引用的嵌套一般发生在定义宏的宏上面。下面是书中的一个例子:定义一个宏的简称的的宏。
因为一些CL的名字相当的长,比如destructuring-bind 和multiple-value-bind,所以我们可以定义宏来减少我们输入的字符
(defmacro dbind (&rest args)
`(destructruing-bind ,@args))
和
(defmacro mvbind (&rest args)
`(multiple-value-bind ,@args))
就可以了。我们可以看到dbind和mvbind是何等的相似。对于Lisp来说,宏是抽象和消除重复的好方法,那么我们为什么不再定义一个宏来
消除重复呢?假设我们想要得到一个abbrev宏,它允许我们使用(abbrev mvbind mutiple-value-bind)来定义缩写mvbind。下面是这个宏的定义:
(defmacro abbrev (short long)
`(defmacro ,short (&rest args)
`(,',long ,@args)))
卧槽,(,',XXXX),到这一步,我相信初学Lisper肯定凌乱了。其实我何尝不是呢。下面让我们一步一步分析这个宏定义是怎么来的。
我们可以从它的展开式开始,我们最终要一个如下的展开式:
(defmacro mvbind (&rest args)
`(multiple-value-bind ,@args))
我们如果先把multiple-value-bind从反引用中拉出来的话,推到就容易一点,得到如下等价的定义
(defmacro mvbind (&rest args)
(let ( (name 'multiple-value-bind ))
`(,name ,@args) ) )
现在我们将这个展开式转化为一个模板。我们把反引用放到前面,然后将可变的表达式变为一个变量
`(defmacro ,short (& rest args)
(let (( name ',long ))
`(,name ,@args) ) )
最后一步,我们把name 从内层反引用中消除,得到abbrev的宏的主体:
`(defmacro ,short (&rest args)
`(,',long ,@args) ) )
下面我们来正向分析,来展开abbrev宏,例如(abbrev mvbind mutiple-value-bind)
第一步:
首先展开最内层的反引用,和第一个逗号,得到结果
`(DEFMACRO ,SHORT (&REST ARGS) (LIST* ',LONG ARGS))
分享到:
相关推荐
- Alt+BackQuote(`):显示工作区的弹出窗口。 - Ctrl+Alt+J:表达式模板。 - Ctrl+J:快速文档查找。 - Alt+[0-9]:打开预设的动作。 - Ctrl+S:保存全部。 - Ctrl+Alt+Y:同步项目。 - Ctrl+Shift+F12:隐藏所有...
- **Alt + BackQuote (`)**:显示版本控制的快速弹出菜单。 5. **Live Templates**: - **Ctrl + Alt + J**:使用Live Template包围选区。 - **Ctrl + J**:插入预定义的Live Template,例如循环、迭代器等。 6...
- **Ctrl+BackQuote(`)**:快速切换当前方案,适应不同需求。 - **Ctrl+Alt+S**:打开设置对话框,自定义 IDE 配置。 - **Ctrl+Tab**:在标签页和工具窗口间快速切换。 #### 四、运行与调试:测试驱动开发 - **Alt...
- backquote:反引号,一种在某些编程语言中用来表示特定操作的符号。 - backtrace:回溯,通常用于调试程序时追踪程序执行的路径。 - bandwidth:带宽,一般指传输数据的最大速率,也可以指频率范围。 - base case...
- `Alt + BackQuote` (`)`:显示版本控制面板。 12. **通用快捷键** - `Ctrl + S`:保存所有。 - `Ctrl + Alt + Y`:同步项目。 - `Ctrl + Shift + F12`:切换最大化编辑器。 - `Alt + #[0-9]`:打开对应编号...
* Alt + BackQuote(、):弹出快速操作窗口搜索相关快捷键 调试 * Ctrl + Shift + F7:高亮显示所有(选中的)文本 * Ctrl + Alt + F7:选中的字符查找工程出现的地方 * Ctrl + F9:生成项目 * Ctrl + Shift + F9...
- `Alt + BackQuote (`): 快速弹出VCS菜单 - `Ctrl + K`: 将项目提交到版本控制系统 - `Ctrl + T`: 从VCS更新项目 - `Alt + Shift + C`: 查看最近的更改 ### 通用快捷键 - `Double Shift`: 搜索任何地方 - `Ctrl + ...
- ` - backquote(反引号) - ~ - tilde(波浪号) - ! - exclam(感叹号) - @ - at(在) - # - numbersign(号码号),在英式英语中是hash,在美式英语中是pound,在音乐中表示sharp - $ - dollar(美元...
61. `Ctrl + BackQuote(’)`:快速切换当前计划。在多个编辑计划之间切换。 62. `Ctrl + Alt + S`:打开设置页。打开IDE的设置界面,自定义编辑器配置。 63. `Ctrl + Shift + A`:查找编辑器里所有的动作。搜索并...
- ` backquote 反引号 - ~ tilde 波浪号 - ! exclam 感叹号 - @ at 圆圈A - # numbersign 数字标志,在英美口语中,hash或pound - $ dollar 美元符号 - % percent 百分比 - ^ caret 上帽子,指数符号 - & ampersand ...
- **Ctrl+Backquote (`)**:显示当前文件编码。 - **Ctrl+Alt+S**:打开设置/首选项对话框。 - **Ctrl+Alt+Shift+S**:打开项目结构对话框。 - **Ctrl+Shift+A**:显示所有可用的操作。 - **Ctrl+Tab**:切换...
2.14.2. Backquote 2.15. Property Lists 2.16. Keymaps 2.17. Editing Lisp 2.18. Help 2.19. Debugging 2.19.1. The Built-in Debugger 2.19.2. Edebug 2.20. Backups and Auto-Saving 2.21. Evaluating ...
- **反引号** ````:读作“backquote”,在编程语言中用于字符串转义或模板字符串。 - **波浪号** `~`:读作“tilde”,用于表示近似值或语气的轻松。 - **反斜杠** `\`:读作“backslash”,在路径表示或编程中作为...
- **Ctrl+BackQuote(‘)**:快速切换当前方案。 - **Ctrl+Alt+S**:打开设置页面。 - **Ctrl+Shift+A**:查找编辑器里的所有动作。 - **Ctrl+Tab**:在窗口之间进行切换。 以上快捷键总结了 Python 开发者在日常...
- **打开 VCS 快速弹出框**:`Alt+BackQuote(`)` 此快捷键可以快速打开版本控制系统(VCS)相关的快速选择菜单。 - **提交项目到 VCS**:`Ctrl+K` 提交当前项目或文件到版本控制系统中,通常用于 Git 或 SVN。 -...
- **读法**:Backtick 或者 Backquote。 - **应用场景**:在脚本语言中用于执行命令并获取其输出。 #### 16. Opening/Closing Brace { } - **读法**:Opening brace 和 Closing brace。 - **应用场景**:在编程...
- **Ctrl+BackQuote(`)`**:快速切换当前的方案。用于快速切换不同的编辑器方案。 - **Ctrl+Alt+S**:打开设置对话框。用于自定义编辑器的设置和偏好。 - **Ctrl+Alt+Shift+S**:打开项目结构对话框。用于管理项目的...