为什么这些宏的计算结果不一样,导致 \ifx 为真?

为什么这些宏的计算结果不一样,导致 \ifx 为真?

我有一份文件,里面的段落是从一次采访中转录的。每段都来自一位作者,交替出现。从“作者 1”开始,我想在每个段落前面添加作者姓名:

\let\plainpar\par
\def\fs{Author 1}
\def\mw{Author 2}
\def\author{\mw}
\def\par{%
  \wlog{\author==\fs?}
  \ifx\author\fs%
    \wlog{=> setting \mw}
    \def\author{\mw}
  \else
    \wlog{=> setting \fs}
    \def\author{\fs}
  \fi
  \plainpar\author: %
}

Par1

Par2

Par3
\let\par\plainpar
\end

我的想法是重新定义\par,更改作者,然后在段落前打印。但是,输出只在每个段落前面包含“作者 1:”。检查日志,我发现:

This is TeX, Version 3.141592653 (TeX Live 2022/dev/Debian) (preloaded format=tex 2022.7.15)  4 FEB 2023 16:28
**stunde_short.tex
(./stunde_short.tex
Author 2==Author 1?
=> setting Author 1
Author 1==Author 1?
=> setting Author 1
Author 1==Author 1?
=> setting Author 1
 [1] )
Output written on stunde_short.dvi (1 page, 292 bytes).

看起来它第一次运行成功,并且重新定义\author正确。然而,在后续检查中\ifx,计算结果为 false,尽管\author\fs计算结果似乎相同。

我希望得到输出

Author 1: Par1
Author 2: Par2
Author 1: Par3

我遗漏了什么\ifx

答案1

条件\ifx检查以下两个标记而不扩展它们。然后返回 true,前提是

  • 两个标记都是字符标记或\let字符的控制序列,并且它们是相同的(就字符代码和类别代码而言),或者
  • 两个标记都是控制序列,并且是相同的原语(可能名称不同),或者
  • 两个标记都是宏,并且对于 具有相同的状态\outer,并且\long\protected当使用 e-TeX 时),具有相同的参数文本和相同的替换文本。

\wlog你对下面的输出感到困惑全面扩张有时最后一种情况被报告为“具有相同的扩展”,但这非常不精确,因为只比较了替换文本,而没有进一步的扩展。

您的宏\author\ws不满足最后一个条件,因为的一级扩展\author\fs和的一级扩展\fsAuthor 1

我会使用\everypar而不是重新定义\par

\def\interviewer{Interviewer}
\def\interviewee{Interviewee}

\newif\ifinterviewer
\interviewertrue
\def\alternatespeakers{%
  \ifinterviewer
    \interviewer:~
    \interviewerfalse
  \else
    \interviewee:~
    \interviewertrue
  \fi
}

\begingroup

\everypar{\alternatespeakers}

Par 1

Par 2

Par 3

Par 4

\endgroup

\bye

在此处输入图片描述

答案2

正如已经说过的egreg 的回答\ifx比较所谓的后续两个 token 的含义。即,两个后续 token 的收集和比较没有评估/扩展。

例如,\def\author{\fs}命令\show\author在控制台上显示:

> \author=macro:
->\fs .

例如,\def\author{\mw}命令\show\author在控制台上显示:

> \author=macro:
->\mw .

例如,\def\fs{Author 1}命令\show\fs在控制台上显示:

> \fs=macro:
->Author 1.

例如,\def\mw{Author 2}命令\show\mw在控制台上显示:

> \mw=macro:
->Author 2.

如您所见, 的含义\fs和 的含义\mwnever 都不等于 的含义\author,因此和 的\ifx比较永远不会产生“真”分支/总是产生“假”分支。\author\fs

(请注意,\show不要\meaning泄露可能属于令牌含义的每一条信息。

例如

> \mw=macro:
->Author 2.

您不知道字符标记的类别Au,,,,,thor⟨空间⟩2有。)


您可以使用\let分配/\author的含义来解决问题:\fs\mw

(如果您已经定义了控制序列\bar,那么的效果\let\foo=\bar是,从今以后具有执行分配时的\foo含义。可以被认为是执行分配时具有不同“形状”的副本。)\bar\let\foo\bar\let

例如,之后\def\fs{Author 1}\let\author = \fs
\show\fs控制台上显示:

> \fs=macro:
->Author 1.

\show\author控制台上显示:

> \author=macro:
->Author 1.

,即\fs\author的含义相同。

例如,之后\def\mw{Author 2}\let\author = \mw
\show\mw控制台上显示:

> \mw=macro:
->Author 2.

\show\author控制台上显示:

> \author=macro:
->Author 2.

,即\mw\author的含义相同。


因此,在您的场景中您可以执行以下操作:

\let\plainpar\par
\def\fs{Author 1}%
\def\mw{Author 2}%
% initialize \author:
\let\author=\mw
\def\par{%
  \wlog{\author==\fs?}%
  \ifx\author\fs
    \wlog{=> setting \mw}%
    \let\author=\mw
  \else
    \wlog{=> setting \fs}%
    \let\author=\fs
  \fi
  \plainpar\author: %
}%

Par1

Par2

Par3
\let\par\plainpar
\end

在此处输入图片描述

但也\let有其注意事项。例如,一个命令通常由几个宏组成,这些宏形成一个“机制”(或 expl3 术语中的“功能”)。包letltxmacro处理使用 LaTeX 时可能在不知情的情况下遇到的大多数类似情况。


通过这种方式,\csname..\endcsname您可以让 TeX 创建控制序列标记,其名称也包含非字母,如数字或空格。

因此\csname..\endcsname可以轻松地用于将\count寄存器的值映射到特定的控制序列标记/特定控制序列标记的当前扩展。

例如,如果您愿意,可以使用\count-register 并让 TeX define/via 通过\everypar调用相应的参与者宏\csname..\endcsname

%------------------------------------------------------------
% \CsNameToCsToken<stuff not in braces>{NameOfCs}
% ->  <stuff not in braces>\NameOfCs
% (<stuff not in braces> may be empty.)
%............................................................
\long\def\CsNameToCsToken#1#{\InnerCsNameToCsToken{#1}}%
\long\def\InnerCsNameToCsToken#1#2{%
  \expandafter\Exchange\expandafter{\csname#2\endcsname}{#1}%
}%
\long\def\Exchange#1#2{#2#1}%
%------------------------------------------------------------
\CsNameToCsToken\def{participant 1}{First Author}%
\CsNameToCsToken\def{participant 2}{Second Author}%
\CsNameToCsToken\def{participant 3}{Third Author}%
\CsNameToCsToken\def{participant 4}{Fourth Author}%
\def\participantMAX{4}%
\newcount\participantCNT
\participantCNT=0 %
\def\nextparticipant{%
  \global\ifnum\participantCNT<\participantMAX\advance\fi\participantCNT 1 %
  \CsNameToCsToken{participant \the\participantCNT}: %
}%
\def\setparticipant#1{%
  \global\participantCNT=#1\relax
  \global\advance\participantCNT by-1 %
  \ignorespaces
}%


\begingroup
\everypar{\nextparticipant}%

Par1

Par2

Par3

Par4

Par5

Par6

Par7

Par8

\setparticipant{3}
Par9 (Here participant was changed via {\tt\string\setparticipant})
\endgroup

\bye

在此处输入图片描述

使用\numexprε-TeX-extensions 你可以不浪费-register \count

%------------------------------------------------------------
% \CsNameToCsToken<stuff not in braces>{NameOfCs}
% ->  <stuff not in braces>\NameOfCs
% (<stuff not in braces> may be empty.)
%............................................................
\long\def\CsNameToCsToken#1#{\InnerCsNameToCsToken{#1}}%
\long\def\InnerCsNameToCsToken#1#2{%
  \expandafter\Exchange\expandafter{\csname#2\endcsname}{#1}%
}%
\long\def\Exchange#1#2{#2#1}%
%------------------------------------------------------------
\CsNameToCsToken\def{participant 1}{First Author}%
\CsNameToCsToken\def{participant 2}{Second Author}%
\CsNameToCsToken\def{participant 3}{Third Author}%
\CsNameToCsToken\def{participant 4}{Fourth Author}%
\def\participantMAX{4}%
\gdef\participantCNT{0}%
\def\nextparticipant{%
  \xdef\participantCNT{%
    \the\numexpr
      % somehow get a space-token behind \participantMAX
      \ifnum\participantCNT<\participantMAX\Exchange{}{} \participantCNT+\fi
      1%
    \relax
  }%
  \CsNameToCsToken{participant \participantCNT}: %
}%
\def\setparticipant#1{%
  \xdef\participantCNT{\the\numexpr(#1)-1\relax}%
  \ignorespaces
}%


\begingroup
\everypar{\nextparticipant}%

Par1

Par2

Par3

Par4

Par5

Par6

Par7

Par8

\setparticipant{3}
Par9 (Here participant was changed via {\tt\string\setparticipant})
\endgroup

\bye

在此处输入图片描述


然而请注意,延迟/定时扩展 -expressions 有一些注意事项\csname..\endcsname

例如,如果\csname..\endcsname最终在所谓的移动参数中未展开,而该参数也被输出例程用于创建带有大写字母的页眉,则大写例程可能会变成,\csname foobar\endcsname这样就不会出现\csname FOOBAR\endcsname控制字标记,而是\foobar出现控制字标记\FOOBAR,这反过来可能不会触发 TeX 执行您希望 TeX 执行的操作。;-)

相关内容