\g@addto@macro
在循环中使用时我感到困惑。我的代码是:
\documentclass{article}
\usepackage{ifthen}
\usepackage{newfile}
\usepackage{xstring}
\makeatletter
\DeclareRobustCommand\@initial[1]{#1}
\DeclareRobustCommand\initial[1]{%
\StrLeft{#1}{1}[\firstletter]%
\@initial{\firstletter}}
\newcounter{authors@count}
\newcommand\authors[2]{
\stepcounter{authors@count}
\expandafter\gdef\csname @author@first\arabic{authors@count}\endcsname{#1}
\expandafter\gdef\csname @author@last\arabic{authors@count}\endcsname{#2}
}
\newcounter{i}
\def\authorsliststring{}
\def\authorslist{%
\whiledo{\value{i} < \value{authors@count}}{%
\stepcounter{i}%
\@nameuse{@author@last\thei}\space% for test only
\initial{\@nameuse{@author@first\thei}}.% for test only
\g@addto@macro\authorsliststring{%
\@nameuse{@author@last\thei}
\initial{\@nameuse{@author@first\thei}}.}%
\ifthenelse{\thei = \theauthors@count}{}
{,\space% for test only
\g@addto@macro\authorsliststring{, }%
}%
}
}
\newcommand\writetoc{
\newoutputstream{txt}
\openoutputfile{\jobname.txt}{txt}
\addtostream{txt}{\authorsliststring}
\closeoutputstream{txt}
}
\makeatother
\begin{document}
\authors{Alex}{Andersson}
\authors{Barbara}{Brown}
\authors{Charles}{Chaplin}
\authorslist
\authorsliststring
\writetoc
\end{document}
\authors
在开发我的类时,使用重复命令将 First Name 和 Nast Name 分别写入单独的变量中对我来说很方便。稍后,使用单独的\authorslist
宏,我生成\authorsliststring
字符串作为 Last Name 和 First Name 的第一个字符(这里有一个很长的解释,说明为什么我需要在单独的\authorslist
命令中执行此操作以及为什么我需要\initial
这种方式的命令,但我需要这样)。\authorsliststring
然后在类中的正确位置使用该字符串并将其写入文件,以便稍后从其他 TeX 文件生成目录。我需要在\authorsliststring
文件中获取相同的内容 - “Andersson A., Brown B., Chaplin C。”
然而,在屏幕上我看到:
然后我进入文件:
Chaplin \initial {Charles}., Chaplin \initial {Charles}., Chaplin \initial {Charles}.
此外,我不明白如何写入文件,不是\initial {Charles}
,但是C.
。
答案1
宏组合的作用\initial
——\@initial
对我来说并不完全清楚。
似乎\initial
是一种宏机制,它在内部使用\@initial
并\StrLeft
在某些时候定义宏\firstletter
以扩展为作者名字的首字母。
只要在你的\authorslist
-recursion/ \whiledo
-recursion 中你正确地分离了哪些操作是可扩展的,哪些操作是不可扩展的 - 例如,\StrLeft
(因此也是\initial
)不是,你可以结合(ab?)使用的技巧\romannumeral
来保持 TeX 触发扩展,直到遇到\UD@stopromannumeral
交换宏参数的技巧,以便让 TeX一次性排列\g@addto@macro
-directive 的标记以添加名称:\authorsliststring
\documentclass{article}
\usepackage{ifthen}
\usepackage{newfile}
\usepackage{xstring}
\makeatletter
\newcommand\UD@Exchange[2]{#2#1}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
% Assume \initial is a function/macro-mechanism which at some point defines the macro
% \ScratchmacroForFirstLetter to deliver the first letter of the first name.
% ( \initial itself probably is not fully expandable, e.g., due to calling \StrLeft
% or ehatever, which in turn is not fully expandable. In any case in the end
% the macro \ScratchmacroForFirstLetter is defined. )
\newcommand\ScratchmacroForFirstLetter{}%
\@ifdefinable\initial{%
\DeclareRobustCommand\initial[1]{\StrLeft{#1}{1}[\ScratchmacroForFirstLetter]}%
}%
\newcounter{authors@count}
\newcommand\authors[2]{%
\stepcounter{authors@count}%
\global\@namedef{@author@first\arabic{authors@count}}{#1}%
\global\@namedef{@author@last\arabic{authors@count}}{#2}%
}
\newcounter{i}%
\newcommand*\authorsliststring{}%
\newcommand*\authorslist{%
\ifnum\value{i} < \value{authors@count}% Why \whiledo if the recursion can be done without? ;-)
\stepcounter{i}%
% ==============================================================================================
% Typeset the name of the author:
% ----------------------------------------------------------------------------------------------
% - Typeset the first name and a space:
\@nameuse{@author@last\thei} %
% - Somehow get the first letter of the first name of the author into the macro
% \ScratchmacroForFirstLetter - this is the job of \initial:
\initial{\@nameuse{@author@first\thei}}%
% - Apply \ScratchmacroForFirstLetter for typesetting the first letter of the first name of
% the author, trailed by a dot:
\ScratchmacroForFirstLetter.%
% - Probably typeset a comma and a space:
\ifthenelse{\thei = \theauthors@count}{}{, }%
% ==============================================================================================
% Add the name of the author to the macro \authorsliststring:
% ----------------------------------------------------------------------------------------------
% - Somehow get the first letter of the first name of the author into the macro
% \ScratchmacroForFirstLetter - this is the job of \initial:
\initial{\@nameuse{@author@first\thei}}%
% - From now on everything else can be done by means of expansion-methods -
% do some argument-exchanging-trickery via \UD@Exchange for lining up the tokens within
% the brace-group which encloses the last argument of the \g@addto@macro-directive;
% within the brace-group which encloses the last argument of the \g@addto@macro-directive
% use \romannumeral for keeping expansion going until TeX sees \UD@stopromannumeral:
\expandafter\g@addto@macro\expandafter\authorsliststring\expandafter{%
\romannumeral
\ifnum\value{i}=\value{authors@count}\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{\UD@Exchange{}}{\UD@Exchange{, }}%
{%
\expandafter\UD@Exchange\expandafter{%
\ScratchmacroForFirstLetter.%
}{%
\expandafter\expandafter\expandafter\UD@stopromannumeral
\csname @author@last\thei\expandafter\endcsname\@firstofone{ }%
}%
}%
}%
\expandafter\authorslist\fi
}
\newoutputstream{txt}%
\newcommand\writetoc{%
\openoutputfile{\jobname.txt}{txt}%
\addtostream{txt}{\authorsliststring}%
\closeoutputstream{txt}%
}
\makeatother
\begin{document}
\authors{Alex}{Andersson}
\authors{Barbara}{Brown}
\authors{Charles}{Chaplin}
\setcounter{i}{0}%
\authorslist
\texttt{\vrule\string\authorsliststring=\meaning\authorsliststring\vrule}
\authorsliststring
\writetoc
\end{document}
说实话,对我来说这似乎没有必要那么复杂。使用 expl3 而不是 xstring 的\StrLeft
,名字的首字母可能可以通过 获得\str_head_ignore_spaces:n
,它是可扩展的:
\documentclass{article}
\usepackage{newfile}
\ExplSyntaxOn
\clist_new:N \__MyStuff_Authors_clist
\cs_new_protected:Npn \authors #1#2
{
\clist_gput_right:Nx \__MyStuff_Authors_clist
{{#2~\str_head_ignore_spaces:n{#1}.}}
}
\cs_new:Npn \authorslist
{ \clist_use:Nnnn \__MyStuff_Authors_clist {,~}{,~}{,~} }
\ExplSyntaxOff
\newoutputstream{txt}%
\newcommand\writetoc{%
\openoutputfile{\jobname.txt}{txt}%
\addtostream{txt}{\authorslist}%
\closeoutputstream{txt}%
}
\begin{document}
\authors{Alex}{Andersson}
\authors{Barbara}{Brown}
\authors{Charles}{Chaplin}
\noindent
\authorslist
\writetoc
% Let's see the content of \jobname.txt - with older LaTeX-releases
% you might need to add \usepackage{verbatim} to the preamble for
% this to work out, but seems the package newfile already does that
% for you:
\verbatiminput{\jobname.txt}
\end{document}
答案2
如评论中所述,\thei
需要在的参数中展开\g@addto@macro
。在这里,我通过保存完全展开的定义(需要进行几次展开)\thei
来实现这一点,然后在定义中使用足够的s(简写为)来用其展开替换 的出现。\Thei
\expandafter
\z
\Thei
\documentclass{article}
\usepackage{ifthen}
\usepackage{newfile}
\usepackage{xstring}
\makeatletter
\DeclareRobustCommand\@initial[1]{#1}
\DeclareRobustCommand\initial[1]{%
\StrLeft{#1}{1}[\firstletter]%
\@initial{\firstletter}}
\newcounter{authors@count}
\newcommand\authors[2]{
\stepcounter{authors@count}
\expandafter\gdef\csname @author@first\arabic{authors@count}\endcsname{#1}
\expandafter\gdef\csname @author@last\arabic{authors@count}\endcsname{#2}
}
\newcounter{i}
\def\authorsliststring{}
\def\Al{@author@last}
\def\Af{@author@first}
\let\z\expandafter
\def\authorslist{%
\whiledo{\value{i} < \value{authors@count}}{%
\stepcounter{i}%
\edef\Thei{\thei}%
% \@nameuse{\Al\thei}\space% for test only
% \initial{\@nameuse{\Af\thei}}.% for test only
\z\z\z\g@addto@macro\z\z\z\authorsliststring\z\z\z{\z\z\z%
\@nameuse\z\z\z{\z\z\z\Al\z\Thei\z}\z\ \z
\initial\z{\z\@nameuse\z{\z\Af\Thei}}.}%
\ifthenelse{\thei = \theauthors@count}{}
{%,\space% for test only
\g@addto@macro\authorsliststring{, }%
}%
}
}
\newcommand\writetoc{
\newoutputstream{txt}
\openoutputfile{\jobname.txt}{txt}
\addtostream{txt}{\authorsliststring}
\closeoutputstream{txt}
}
\makeatother
\begin{document}
\authors{Alex}{Andersson}
\authors{Barbara}{Brown}
\authors{Charles}{Chaplin}
\authorslist
\authorsliststring
\writetoc
\end{document}