我有一份文件,里面的段落是从一次采访中转录的。每段都来自一位作者,交替出现。从“作者 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
和的一级扩展\fs
是Author 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
和 的含义\mw
never 都不等于 的含义\author
,因此和 的\ifx
比较永远不会产生“真”分支/总是产生“假”分支。\author
\fs
(请注意,\show
不要\meaning
泄露可能属于令牌含义的每一条信息。
例如
> \mw=macro:
->Author 2.
您不知道字符标记的类别A
。u
,,,,,t
h
o
r
⟨空间⟩,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 执行的操作。;-)