问题来源
在看刘海洋的《latex入门》的时候,关于\def
和\edef
这两个命令,作者在书中对于这两个命令的区别有如下描述:
edef也可以用来定义宏,只是它会首先完全展开定义中的内容,然后把展开的结果作为命令的定义,而def命令就是把后面花括号里面的内容直接作为命令的定义。
然后作者还举了一个例子来说明这两个宏,如下:
\def\multi#1{(#1, #1)}
\edef\multi#1{[\multi{#1}, \multi{#1}]}
\multi{X}
结果为:
[(X, X), (X, X)]
至此,作者对于这两个命令没有过多描述了。当然我不能说这是错的,但是真的想对这两个命令的概念糊弄过去的话,就没意思了。比如心中肯定会冒出来这么一个问题,当使用\multi
这个新定义的命令时,为什么要调用第二个,而不调用第一个?这里的\edef
刚好解决了本例中的递归的问题,但是解决递归问题不代表当我使用\multi
命令的时候就一定要使用递归的版本。于是乎,我的脑洞就开始发散了。
我的分析
在这个问题中,就可以把latex代码看成一个黑盒,多些几句测试语句就可以了。
\def\multi#1{(#1, #1)}
\edef\multi#1{[\multi{#1}, \multi{#1}]}
\multi{X}
这就是上面的代码,从这个代码中,可以得出如下结论:
由于最终的结果是
[(X, X), (X, X)]
,说明使用\multi
命令的时候,先调用了递归的版本,后调用了非递归的版本,说明这两个命令都存在,即第二个def中的命令没有覆盖第一个def中的命令。
添加一句代码看看情况~
\def\multi#1{(#1, #1)}
\edef\multi#1{[\multi{#1}, \multi{#1}]}
\edef\multi#1#2{[\multi{#1}, \multi{#2}]}
\multi{X}
很常见的情况,报错了~,但是如果把其中的代码改成\multi{X}{Y}
的形式,就没有错,且结果如下:
[[(X, X), (X, X)], [(Y, Y), (Y, Y)]]
这就说明,当定义了两个参数的\multi
方法以后,一个参数的\multi
方法已经不可见了,但是从结果来看,确实有在运行,所以有一个熟悉的数据结构出现了,那就是栈。可以想象,同样的命令名,多次定义,就是一个命令压栈的过程,只有最后定义的命令是可见的,而之前定义的命令是不可见的,而且这里还猜测,如果一个命令内部调用了和本命令名相同的命令,就会去栈的下一层去寻找命令来执行,这里还可以举一个奇怪的例子来说明:
\def\multi#1{(#1, #1)} -> command1
\edef\multi#1{[\multi{#1}, \multi{#1}]} -> command2
\edef\multi#1{[\multi{#1}, \multi{#1}]} -> command3
\multi{X}
可以看出,我把形式完全相同的命令定义了两次,由于可见的命令只有最后一个,所以结果为:
[[(X, X), (X, X)], [(X, X), (X, X)]]
即\multi
命令被调用的时候,调用的命令是command3
,在command3
是使用\edef
进行定义的,所以会首先从栈中查找\multi
命令,这里查找到了command2
,同理,command2
查找到了command1
,最后的结果如上所示。
最后这里再探究一个小点,即究竟是把所有的用户自定义的命令分析完了以后再来执行所有的命令还是逐条解释,用下面的代码来测试:
\def\multi#1{(#1, #1)}
\edef\multi#1{[\multi{#1}, \multi{#1}]}
\multi{X}
\edef\multi#1{[\multi{#1}, \multi{#1}]}
\multi{X}
最后的测试结果为:
[(X, X), (X, X)]
[[(X, X), (X, X)], [(X, X), (X, X)]]
结论很明显了,方式为逐条解释。
结论
看书的时候还是要多思考一下,如果发现了不懂的地方,这个坑能填的还是要尽量填,但是同时也要把握好不求甚解的度,对于一些边角料的知识,有多余的时间和精力的时候再来分析不迟,溜了溜了~