宏可以访问它自己的名字吗?

宏可以访问它自己的名字吗?

两个宏(具有相同的主体,因此比较为\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_cslastexpanded_chr在程序(第 389 节)中设置 ,它们在扩展macro_call时在第 472 节中使用 。\lastexpanded

现在,您可以将mytex.ch作为最后一项添加到源代码中pdftex_ch_srcs 的列表中。然后您可以运行。新的二进制文件已编译。您可以尝试:Makefilesource/build/texk/web2c/pdftexmake pdftexpdftex

 ./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" 

相关内容