两个宏(具有相同的主体,因此比较为\ifx
真)是否会根据其名称而具有不同的行为?
\documentclass{article}
\newcommand\red{some code for: if my name is red "red" else "not red" fi}
\let\blue\red
\begin{document}
\ifx\red\blue true \else false \fi %must give true
\red %prints red
\blue %prints not red
\end{document}
我认为答案是否定的,但也许其中有一些我不知道的技巧。只对某些引擎有效的答案是可以的。
答案1
不。必须展开宏才能显示和执行代码。但展开会用定义文本替换宏标记,这意味着原始宏已消失,并且不会留下任何痕迹来显示代码的来源。
*如果\tracingmacros
处于活动状态,则.log
文件会告知扩展步骤,但\tracingmacros
在宏本身中启用已经太晚了。此外,.log
从 TeX 内部读取文件并不容易,因为.log
文件块可能尚未写入(缓冲输出/操作系统相关行为/...),并且在第二次运行中文件.log
将被覆盖。
答案2
我不知道这个问题的起源(两个宏从
\ifx
角度上看是相同的,但就其名称而言,它们的行为不同)。但我想介绍修改 TeX 源以解决此任务的方法。我不指望有人会使用这个补丁。这只是说明这种修改有多容易或多困难。让我先引用一下:
当然,如果我自己从事出版业,到现在为止我可能已经有十个不同版本的 TEX,用于十个不同的复杂项目。它们看起来几乎都与 TEX 相同,但其他人不会有这个程序——他们不需要它,他们没有做我的出版社正在做的那本书。
唐纳德·E·克努斯,布拉格,1996 年 3 月
我们需要添加新的原语\lastexpanded
,将其扩展为最后一个扩展宏的名称,就像\string
它一样。这意味着
\TeX \edef\a{\lastexpanded} \edef\b{\string\TeX}
now: \a is equal to \b
我们通过创建mytex.ch
更改文件来实现这一点。这是源文件的“补丁”
tex.web
。我们首先仔细翻阅 C&T(TeX:程序)的第二卷,并仔细思考它。然后我们创建以下文件:
% This is a modification of TeX inspired by
% http://tex.stackexchange.com/questions/130285/has-a-command-access-to-its-own-name
% This modification adds new primitive \lastexpanded which expands to the
% \string of the last expanded macro name.
\S 297 declaration of global variables
@x
@!cur_tok: halfword; {packed representative of |cur_cmd| and |cur_chr|}
@y
@!cur_tok: halfword; {packed representative of |cur_cmd| and |cur_chr|}
@!lastexpanded_cs: pointer; {variable for \.{\\lastexpanded}}
@!lastexpanded_chr: halfword; {|cur_chr| for \.{\\lastexpanded}}
@z
\S 389 macro_call saves its name
@x
warning_index:=cur_cs; ref_count:=cur_chr; r:=link(ref_count); n:=0;
@y
warning_index:=cur_cs; ref_count:=cur_chr; r:=link(ref_count); n:=0;
lastexpanded_cs:=cur_cs; lastexpanded_chr:=cur_chr;
@z
\S 439 default values
@x
cur_val:=0; cur_val_level:=int_val; radix:=0; cur_order:=normal;
@y
cur_val:=0; cur_val_level:=int_val; radix:=0; cur_order:=normal;
lastexpanded_cs:=0; lastexpanded_chr:=0;
@z
\S 468 adding new expandable primitive \lastexpanded
@x
@d job_name_code=pdftex_convert_codes {command code for \.{\\jobname}}
@y
@d job_name_code=pdftex_convert_codes {command code for \.{\\jobname}}
@d lastexpanded_code=pdftex_convert_codes + 1 {command code for
\.{\\lastexpanded}}
@z
@x
primitive("jobname",convert,job_name_code);@/
@!@:job_name_}{\.{\\jobname} primitive@>
@y
primitive("jobname",convert,job_name_code);@/
@!@:job_name_}{\.{\\jobname} primitive@>
primitive("lastexpanded",convert,lastexpanded_code);@/
@!@:lastexpanded_}{\.{\\lastexpanded} primitive@>
@z
\S 469 printing about new primitive
@x
othercases print_esc("jobname")
@y
lastexpanded_code: print_esc("lastexpanded");
othercases print_esc("jobname")
@z
\S 470 the c declaration
@x
@!c:number_code..job_name_code; {desired type of conversion}
@y
@!c:number_code..lastexpanded_code; {desired type of conversion}
@z
\S 471 Scanning argument, \lastexpanded has no argument
@x
job_name_code: if job_name=0 then open_log_file;
@y
job_name_code: if job_name=0 then open_log_file;
lastexpanded_code: do_nothing;
@z
\S 472
@x
job_name_code: print(job_name);
@y
job_name_code: print(job_name);
lastexpanded_code: if lastexpanded_cs<>0 then sprint_cs(lastexpanded_cs)
else print_char(lastexpanded_chr);
@z
新的可扩展原语被添加到第 468 节中。这里有点复杂,因为我们的补丁将应用于pdftex
:有许多新的原语,这就是为什么最后一个代码的值job_name_code
与原始 TeX 中的值不同。全局变量lastexpanded_cs
和lastexpanded_chr
在程序(第 389 节)中设置 ,它们在扩展macro_call
时在第 472 节中使用 。\lastexpanded
现在,您可以将mytex.ch
作为最后一项添加到源代码中pdftex_ch_srcs
的列表中。然后您可以运行。新的二进制文件已编译。您可以尝试:Makefile
source/build/texk/web2c/
pdftex
make pdftex
pdftex
./pdftex -ini
**\relax
*\show\lastexpanded
> \lastexpanded=\lastexpanded.
你可以通过 生成格式./pdftex -ini -jobname pdftex -etex pdfetex.ini
并在同一目录中使用它./pdftex test
。如下test.tex
所示:
\edef\redname{\string\red}
\def\red{\edef\myname{\lastexpanded}%
\ifx\myname\redname \message{I am RED}%
\else \message{I am NOT red}%
\fi
}
\let\foo=\red
\red % the output: I am RED
\foo % the output: I am NOT red
\ifx\red\foo \message{YES}\fi % the otput: YES
\end
答案3
正如 Heiko 所说,仅使用宏是无法做到这一点的,我认为使用看起来像宏但实际上不是宏的东西可以做到这一点。
我想到的方法是确定一个应用于每种颜色的前缀字符,并使其处于活动状态。然后,您就可以从该活动字符中获取颜色名称的标记,并执行您所描述的操作。
例如,使用“/”作为活动字符,然后您可以使用以下方式声明颜色
/red % Gives "red"
/blue % Gives "not red"