如何使用循环创建定义数量的新命令来计算作者

如何使用循环创建定义数量的新命令来计算作者

对于我的模板,我想创建定义多个作者的可能性,并自动创建相应数量的变量/命令,以保存任何作者的姓名并使其能够在以后打印它们。代码必须在序言中。

\varname{...}为了创建可以打印的不同变量/命令\printvarname,我随机找到了以下代码在 tex.SE 上效果很好:

\makealetter
\newcommand{\NewVariable}[1]{%
    \expandafter\newcommand\csname #1\endcsname[1]{\@namedef{@#1}{##1}}
    \@namedef{@#1}{}
    \expandafter\newcommand\csname print#1\endcsname{%
        \ifthenelse{\equal{\csname @#1\endcsname}{}}{}{\csname @#1\endcsname}}
}
\makeatother

现在我想创建一个循环,自动创建这些语法命令的预定义数量\authorone\authortwo等等。对于作者数量的定义,我使用一个简单的命令:\newcommand{\setauthors}[1]{\def\numauthors{#1}}。一个问题似乎是命令名不能采用整数,但我需要整数来定义作者的数量。所以我使用包fmtcount将它们转换为单词。

我尝试了该网站(、、foreach)中的不同方法,但都无法正常工作。@forxintFor

以下是带有一些方法的 MWE。我评论了我的测试循环并添加了一个如何使用它的简单示例:

\documentclass[%
]{article}
\usepackage[T1]{fontenc}

\usepackage{ifthen}
\makeatletter % command to set multiple persistent variables with content
\newcommand{\NewVariable}[1]{%
    \expandafter\newcommand\csname #1\endcsname[1]{\@namedef{@#1}{##1}}
    \@namedef{@#1}{}
    \expandafter\newcommand\csname print#1\endcsname{%
        \ifthenelse{\equal{\csname @#1\endcsname}{}}{}{\csname @#1\endcsname}}
}
\makeatother 

\usepackage{pgffor}
\usepackage{fmtcount} % to convert integers in words: e.g. 1 into one
\usepackage{xinttools}

\newcommand{\setauthors}[1]{\def\numauthors{#1}}

\setauthors{3} %set number of authors

%\foreach \x in {1,...,\numauthors}{%
%       \NewVariable{author\numberstringnum{\x}}
%       }

%\xintFor #1 in {\xintSeq{1}{\numauthors}} \do {\NewVariable{author\numberstringnum{#1}}}
% also tried the starred version \xintFor*

%\makeatletter % I know the definition of a range from 1 to \numauthors is not correct, its just a placeholder
%\@for\authorcount:1,...,\numauthors\do{\NewVariable{author\numberstringnum{\authorcount}}}
%\makeatother

\NewVariable{authorone} % example for defined variable/command and how it should work

\authorone{Jimmy Buckets}

\begin{document}

\printauthorone

\end{document}

expl3其他软件包(还有版本,我对这些了解得更少)似乎有更多循环变体,但我更喜欢一种对所需额外软件包要求最少的解决方案。尽管如此,最终重要的是它能正常工作。未来的用户应该只添加一个数字\setauthors并填写生成的作者变量。所有其他东西都应该自动化。也可以选择先将作者编号分配给数组...

我猜这是嵌套命令的扩展问题(NewVariable,,numberstring...环形)。不幸的是,扩展序列/顺序对我来说仍然是一本难懂的书,因为它与我(稍微)习惯的大多数编程语言有很大不同。

编辑

使用@Alans 的出色答案,包括 orcid。使用预定义命令获取包括符号的 Orcid 链接,创建有效 Orcid 的expl3命令效果更好:\cs_new_protected:Nn \l_lukeflo_get_orcid:n

\usepackage{orcidlink}
\newcommand{\orcidid}[1]{\orcidlink{#1}\space\href{https://orcid.org/#1}{#1}}
...
\cs_new_protected:Nn \l_lukeflo_get_orcid:n {
    \tl_set:Nx \l_tmpa_tl {\seq_item:Nn \l_lukeflo_orcid_seq {#1}}
    \orcidid{\l_tmpa_tl}\par
}

否则,将为链接href添加不必要的扩展。请参阅我在 Alans 回答下方的评论。.pdf

我已经感谢你的帮助了

答案1

您在问题和评论中陈述的目标是降低包或类的用户需要使用的标记的复杂性。在我看来,从用户的角度来看,创建多个不同的命令名称是非常违反直觉的。因此,这里有一个实现相同目标的方法,但使用最简单的用户语法:以简单的逗号分隔列表形式输入作者、所属机构和电子邮件的命令,使用键值设置命令实现的一些格式化命令(可能需要或不需要面向用户)和打印元素的命令。我使用了expl3内置于 LaTeX 内核的方法,因此没有涉及额外的包。这个答案旨在展示可以用这些方法轻松完成的事情。

在此示例中,唯一需要面向用户的命令是\authors\affiliations\emails命令。其余命令可能需要面向用户,也可能不需要,具体取决于您的实际使用情况。由于作者通常有多个隶属关系,因此可以将它们作为用括号括起来的列表输入。我还添加了一个\orcids命令并显示了代码以生成 ORCID 徽标和链接。

\documentclass{article}
\usepackage{orcidlink}
\ExplSyntaxOn
% First define three sequences for storing the data
\seq_new:N \l_lukeflo_authors_seq
\seq_new:N \l_lukeflo_email_seq
\seq_new:N \l_lukeflo_affil_seq
\seq_new:N \l_lukeflo_orcid_seq 
% Now set up some keys for modifying the output if needed
\keys_define:nn{mypackage}{
    author.tl_set:N = \l_lukeflo_authformat_tl,
    affil.tl_set:N = \l_lukeflo_affilformat_tl,
    email.tl_set:N = \l_lukeflo_emailformat_tl,
}
% User command to set the formatting keys.
\NewDocumentCommand{\authorSetup}{m}{
    \keys_set:nn {mypackage} {#1}}

% Set up the defaults
\authorSetup{author=\itshape,affil={},email=\ttfamily}

% User command to enter authors (comma separated)
\NewDocumentCommand{\authors}{m}{
    \seq_set_from_clist:Nn \l_lukeflo_authors_seq {#1}
}
% User command to enter affiliations (comma separated; multiple affiliations with {})
\NewDocumentCommand{\affiliations}{m}{
    \seq_set_from_clist:Nn \l_lukeflo_affil_seq {#1}
}
% User command to enter emails
\NewDocumentCommand{\emails}{m}{
    \seq_set_from_clist:Nn \l_lukeflo_email_seq {#1}
}
\NewDocumentCommand{\orcids}{m}{
    \seq_set_from_clist:Nn \l_lukeflo_orcid_seq {#1}
}
% internal command to map print affiliations
\cs_new_protected:Nn \lukeflo_get_affils:n {
    \clist_set:Nx \l_tmpa_clist {\seq_item:Nn \l_lukeflo_affil_seq {#1}}
    \clist_map_inline:Nn \l_tmpa_clist {\l_lukeflo_affilformat_tl {##1}\par}
}
\cs_new_protected:Nn \lukeflo_get_orcid:n {
    \tl_set:Nx \l_tmpa_tl {\seq_item:Nn \l_lukeflo_orcid_seq {#1}}
    \orcidlink{\l_tmpa_tl}\space\l_lukeflo_emailformat_tl
    \href{https\c_colon_str//orcid.org/\l_tmpa_tl/}{\l_tmpa_tl}\par
}

% internal command to print an author/affiliations/email block
\cs_new_protected:Nn \lukeflo_map_author_info:nn {
    {\parindent=0pt
    {\l_lukeflo_authformat_tl\seq_item:Nn \l_lukeflo_authors_seq {#1}\par}
    {\lukeflo_get_affils:n {#1}}
    {\l_lukeflo_emailformat_tl\seq_item:Nn \l_lukeflo_email_seq {#1}\par}
    {\lukeflo_get_orcid:n {#1}}
    \vspace{\baselineskip}
    }
}
% User command to print all the author blocks
\NewDocumentCommand \printauthors {} {
    \seq_map_indexed_function:NN \l_lukeflo_authors_seq \lukeflo_map_author_info:nn
}
\ExplSyntaxOff
\authors{Margaret Atwood, Zadie Smith, Madeleine Thien}
\affiliations{University of Toronto,{New York University,Columbia University},Brooklyn College}
\emails{[email protected], [email protected],[email protected]}
\orcids{0000-0000-0000-0000,0000-0000-0000-0000,0000-0000-0000-0000}

\begin{document}
\printauthors
\authorSetup{author=\bfseries}
\printauthors
\end{document}

代码输出

答案2

问题 1:

您使用包 fmtcount,由于多语言支持需要在文档环境开始时检测语言,因此该包的宏在序言中不起作用。

问题2:

pgffor 的\foreach每次迭代都在局部范围内进行,因此宏定义在外部无法存在\foreach

问题3:

您需要处理\NewVariable在将其传递给替换文本时不扩展其参数的情况,其中它不是立即执行,而是在执行\newcommand根据定义的命令时执行。\newcommand

问题4:

片段

 \@namedef{@#1}{}
 \expandafter\newcommand\csname print#1\endcsname{%
   \ifthenelse{\equal{\csname @#1\endcsname}{}}{}{\csname @#1\endcsname}}

似乎有些无意义:
来自的控制序列\csname @#1\endcsname被初始化为空。 -
command \print...via\ifthenelse检查该控制序列是否提供空,如果是,via 分支提供空,这也可能来自扩展该控制序列,而无需检查。;-)



我理解您更喜欢命令而不是表示变量名称的字符串:命令不受\uppercase/之类的东西的影响lowercase,而表示变量名称的字符串可能会受到影响。(例如,\uppercase可能会将变量名称字符串“author 1”变成“AUTHOR 1”,这可能是一个问题。)


如果您不介意在文档环境中定义变量,您可以尝试这样的操作:

\documentclass[%
]{article}

\usepackage{xinttools}
\usepackage{fmtcount}
\FCloadlang{english}


% command to set multiple persistent variables with content
\makeatletter
\@ifdefinable\NewVariable{%
  \DeclareRobustCommand{\NewVariable}[1]{%
    \expandafter\@NewVariable
    \csname #1\expandafter\endcsname
    \csname @#1\expandafter\endcsname
    \csname print#1\endcsname
    {#1}%
  }%
}%
\newcommand\@NewVariable[4]{%
  \newcommand#1[1]{\renewcommand*{#2}{##1}}%
  \newcommand*#2{}%
  % In the \else-branch you could have an error-message about undefined variable #4.
  \newcommand*#3{\ifdefined#2\else\expandafter\@gobble\fi#2}%
}%
\makeatother

\begin{document}

  \newcommand{\setauthors}[1]{\def\numauthors{#1}}%
  \setauthors{3}% set number of authors
  \xintFor* #1 in {\xintSeq{1}{\numauthors}} \do {%
    \storenumberstringnum{scratchy}{#1}%
    \NewVariable{author\FMCuse{scratchy}}%
  }%

  \authorone{Me}
  \authortwo{myself}
  \authorthree{I}

  \printauthorone, {\printauthortwo} and \printauthorthree.

\end{document}

在此处输入图片描述

但我不喜欢这样,因为这样控制序列的名称取决于引入变量时使用的语言,因此每次引入另一个变量时都需要确保正确的语言设置。


如果您不介意使用字符串表示变量名称并加载包文本大小写并使用该包的命令\NoCaseChange来防止\MakeUppercase/\MakeLowercase更改变量名称的字母大小写(在可能需要的边缘情况下),那么您可以这样做。像这样:

\documentclass{article}

\makeatletter
%%===============================================================================
%% Obtain control sequence token from name of control sequence token:
%%===============================================================================
%% \CsNameToCsToken<stuff not in braces>{NameOfCs}
%% ->  <stuff not in braces>\NameOfCs
%% (<stuff not in braces> may be empty.)
\@ifdefinable\CsNameToCsToken{%
  \long\def\CsNameToCsToken#1#{\romannumeral\InnerCsNameToCsToken{#1}}%
}%
\newcommand\InnerCsNameToCsToken[2]{%
  \expandafter\Exchange\expandafter{\csname#2\endcsname}{\@stopromannumeral#1}%
}%
\@ifdefinable\@stopromannumeral{\chardef\@stopromannumeral=`\^^00}%
\newcommand\Exchange[2]{#2#1}%
\makeatother

\CsNameToCsToken\newcommand*{Author 1}{Me}
\CsNameToCsToken\newcommand*{Author 2}{MYSELF}
\CsNameToCsToken\newcommand*{Author 3}{I}

\begin{document}
\CsNameToCsToken{Author 1},
\MakeLowercase{\CsNameToCsToken{Author 2}} and
\CsNameToCsToken{Author 3}.

\CsNameToCsToken\renewcommand*{Author 1}{Myself}
\CsNameToCsToken\renewcommand*{Author 2}{I}
\CsNameToCsToken\renewcommand*{Author 3}{me}

\CsNameToCsToken{Author 1},
\CsNameToCsToken{Author 2} and
\CsNameToCsToken{Author 3}.

\end{document}

在此处输入图片描述


如果您需要维护一个包含每个作者数据记录的数据库,请考虑使用包数据工具

相关内容