我正在为期刊开设一个新课程。
在这个类中,我定义了插入文章作者的命令。我预计在基本形式下一切都能完美运行,也就是说,如果作者姓名以这种方式插入:
\author[1]{Federico Tramarin}
其中索引([1])仅用于与从属关系链接,并且它运行正常,因此我们不应该担心这一点。
出于编辑原因,我被要求要求作者也插入他们姓名的缩写形式。例如:John Smith 和 J. Smith。
为了适应这一点,我创建了一个键值定义,以便允许以这种方式插入作者:
\author[1]{name=Federico,surname=Tramarin,abbreviation=F. Tramarin}
这些键的定义方式很简单:
\def\@tmpauthname{}
\define@key{authornames}{name}{%
\def\@author@name{#1}}
\define@key{authornames}{surname}{%
\def\@author@surname{#1}}
\define@key{authornames}{abbreviation}{%
\def\@authorshortname{#1}}
然后我修改了现有的宏
- 使用第二个参数来设置键的值
- 为完整作者姓名创建一个临时字符串
- 使用它代替第二个参数
供您参考,该宏实际上定义了两个宏:
\@AIauthors
- 创建标题后要排版的最终经典字符串,其中名称和上标包含隶属标记等。\@AIauthorsNames
- 创建一个更简单的字符串,其中仅包含用“;”分隔的全名。
然后在使用 排版标题页时调用这两个宏\maketitle
。
我在这里放了我正在使用的代码。请注意,下面的代码几乎与工作代码(允许仅插入一个带有名称的参数的代码)相同,因为每次您看到\@tmpauthname
原始(和工作)代码时,它都包含一个#2
。
\newcounter{auth}
\def\@@author[#1]#2{%
\setkeys{authornames}{#2}%
\gdef\@tmpauthname{\@author@name\space\@author@surname}%
\typeout{Debug - @@author macro 1-> auth:\@tmpauthname}
\g@addto@macro\@AIauthors{%
\def\baselinestretch{1}%
\refstepcounter{auth}% increment by 1
\newif\ifnotyetprocessed% this if is needed to check if this author has already been counted for corresponding
\notyetprocessedtrue
\typeout{Debug - @@author macro 2-> auth:\@tmpauthname}
\authorsep\@tmpauthname\unskip\textsuperscript{%
\@for\@@affmark:=#1\do{%
%\typeout{Debug - @@author macro -> auth:\theauth\ affmark:\@@affmark - author:#1}
\edef\affnum{\@ifundefined{X@\@@affmark}{affn?}{\AIrefMark{\@@affmark}}}%
\unskip\sep\affnum%
\ifnotyetprocessed%
\NewAIaffLabel{auth-\theauth}{+}%
\fi%
\edef\cormark{\@ifundefined{X@cor-\theauth}{}{%
\if@showcorr
{,\AIrefMark{cor-\theauth}}%
\else
{}%
\fi}}%
% \unskip\sep\cormark\let\sep=,% this line was wrong, since it will add a extra comma in case of multiple affiliations
\unskip\cormark\let\sep=,% now the issue is for the corresponding author with multiple affiliations
\ifx\cormark\@empty%
\else
\ifnotyetprocessed% if the author is corresponding and is the first time we are here
\g@addto@macro\@CorrespondingAuthorName{\@tmpauthname}% add the author name to the output
\notyetprocessedfalse %if it is a subsequent round, do nothing
\fi
\fi
}%
}%
\gdef\authorsep{\unskip,\space}%
\global\let\sep\@empty%
}
\ifx\@AIauthorsNames\@empty
\typeout{Debug - @@author macro 3-> auth:\@tmpauthname}
\g@addto@macro\@AIauthorsNames{\@tmpauthname}%
\typeout{Debug - @@author macro 3-> AIauth:\@AIauthorsNames}
\else
\typeout{Debug - @@author macro 4-> auth:\@tmpauthname}
\g@addto@macro\@AIauthorsNames{;\space\@tmpauthname}%
\typeout{Debug - @@author macro 4-> AIauth:\@AIauthorsNames}
\fi
}
问题是,经过这个我认为微不足道的修改后,我得到了一个只有姓氏重复的字符串n次(与n作者人数)。
正如您所看到的,我插入了一些 \typeout 来进行非常基本的调试以查看发生了什么。
我在日志中看到的内容是这样的:
- 对宏进行第一次扫描,其中
\@tmpauthname
包含当前作者的姓名,但跳过了 内的所有部分\g@addto@macro\@AIauthors{%
。实际上,如果您查看调试消息中的数字,我会看到数字 1 和 3 或 4。 - 对宏进行第二次扫描(我在调试中看到数字 2)执行,
\g@addto@macro\@AIauthors{%
但副作用是这次仅使用保存的最后一个值来执行\@tmpauthname
。
我已经尽我所能尝试了,但还是无法解决问题。我的理解(希望正确)是,\@AIauthors
稍后会评估,因为只有当我请求标题页时才需要它,因此 latex 仅在需要时才扩展该部分。不幸的是,这种情况只发生在指定所有作者后,当它扩展的值时,后者仅包含最后一个值。当考虑\@tmpauthname
经典论点时,这显然不会发生。#2
在我看来,我需要一种方法来告诉 LaTeX 在插入每个新作者后立即评估所有内容,但我不知道 1) 如何实现这一点,2) 这是否是正确的解决方案。
我请求您的帮助来解决这个问题,这让我抓狂并且已经尝试了一整天(这显然是由于我对宏定义的无知)。
非常感谢您给予我任何帮助。
谨致问候 Federico
答案1
当询问与错误跟踪和修改代码有关的问题时,请始终提供最小可重现示例(MRE)/最小完整可验证示例(MCVE),以便测试对代码的更改是否真的解决了问题!
如果你不这样做,那么愿意提供帮助的人注定会猜测他们的尝试/修改可能会解决问题,但这实际上不是一个有效的解决问题的策略。
这次我会做一些猜测。
但这样做的时候我感觉不太舒服。
由于使用,\g@addto@macro
我假设它与 LaTeX 2ε 有关。
你说:
我在这里放了我正在使用的代码。请注意,下面的代码几乎与工作代码(允许仅插入一个带有名称的参数的代码)相同,因为每次您看到
\@tmpauthname
原始(和工作)代码时,它都包含一个#2
。
为什么不去替换#2
而是\@tmpauthname
“外包”那些代码部分,将#2
工作代码中曾经保存的内容保存到持久辅助宏中,并调用该持久辅助宏,同时将工作宏中曾经形成的那些标记作为其第二个#2
参数提供?
可能是这样的——未经测试,因为您没有提供可以测试事物的最小可重现/完整且可验证的示例!!!:
\newcounter{auth}
\newif\ifnotyetprocessed
\def\@@author[#1]#2{%
\setkeys{authornames}{#2}%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begingroup
% In LaTeX 2e \toks@ is a token register.
\toks@{\endgroup\PERSISTINGHELPERMACRO{#1}}%
\protected@edef\TEMPORARYHELPERMACRO{%
\the\toks@{\@author@name\space\@author@surname}%
}%
\TEMPORARYHELPERMACRO
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Alternatively, if \expanded and \unexpanded are available:
%\begingroup
%\let\protect\@unexpandable@protect
%\expanded{%
% \endgroup
% \unexpanded{\PERSISTINGHELPERMACRO{#1}}{\@author@name\space\@author@surname}%
%}%
}%
\long\def\PERSISTINGHELPERMACRO#1#2{%
\g@addto@macro\@AIauthors{%
\def\baselinestretch{1}%
\refstepcounter{auth}% increment by 1
\notyetprocessedtrue
\authorsep#2\unskip
\textsuperscript{%
\@for\@@affmark:=#1\do{%
\edef\affnum{%
\@ifundefined{X@\@@affmark}{affn?}{\AIrefMark{\@@affmark}}%
}%
\unskip\sep\affnum
\ifnotyetprocessed
\NewAIaffLabel{auth-\theauth}{+}%
\fi
\edef\cormark{%
\@ifundefined{X@cor-\theauth}{}{%
\if@showcorr
{,\AIrefMark{cor-\theauth}}%
\else
{}%
\fi
}%
}%
\unskip\cormark
\let\sep=,% now the issue is for the corresponding author with multiple affiliations
\ifx\cormark\@empty
\else
\ifnotyetprocessed % if the author is corresponding and is the first time we are here
\g@addto@macro\@CorrespondingAuthorName{#2}% add the author name to the output
\notyetprocessedfalse % if it is a subsequent round, do nothing
\fi
\fi
}%
}%
\gdef\authorsep{\unskip,\space}%
\global\let\sep\@empty
}%%
\ifx\@AIauthorsNames\@empty
\g@addto@macro\@AIauthorsNames{#2}%
\else
\g@addto@macro\@AIauthorsNames{;\space#2}%
\fi
}%
答案2
您不必担心扩展,只需使用 key=value 解析器即可,它不会将值存储在宏内,而是将它们作为单独的参数转发给另一个宏。
这是通过expkv-cs
(免责声明:我是作者)使用宏\ekvcSplitAndForward
。
请注意,我并没有对您的代码进行太多更改,我只是\@tmpauthname
用转发的正确内容进行了替换#1
(并且由于我们将名称作为正常参数,因此我们不必包含任何扩展控制来实现这一点)。
不过我做了一些改变:
- 用作
\NewDocumentCommand
的前置宏\author
。 - 用于
\NewDocumentCommand
定义\@@author
受保护的。 - 移到
\newif
函数体之外。 - 通过使用辅助工具将
\g@addto@macro
和作者的格式分离\output@author
(这样代码看起来更干净,性能也应该更好)。 - 我删除了你的一些调试消息
- 我把 改为
\g@addto@macro
a 表示\gdef
为空\@AIauthorsNames
- 我通过
%
在某些行尾添加一些不需要的空格来删除。 - 最后,我重新格式化了你的代码以适合我的口味(缩进 2 个空格,代码行不超过 80 个字符(包括注释)...)
\RequirePackage{expkv-cs}
\NewDocumentCommand\author{O{} m}
{\@author{#2}{#1}}
\ekvcSplitAndForward\@author\@@author
{
name = {}
,surname = {}
,abbreviation = {}
}
\ekvcSecondaryKeys\@author
{
% abbreviation is such a long word... Let's define an abbreviated form.
alias abbr = abbreviation
}
\newcounter{auth}
% this if is needed to check if this author has already been counted for
% corresponding
\newif\ifnotyetprocessed
\NewDocumentCommand\@@author{m m m m}
{%
% #1: name
% #2: surname
% #3: abbreviation
% #4: affiliations
% handle mandatory keys (throw an error if they were omitted)
\IfEmptyT{#1#2}
{\PackageError{trama}{Authors must have names, please provide them.}{}}%
\IfEmptyT{#3}
{\PackageError{trama}{Missing name abbreviation.}{}}%
\g@addto@macro\@AIauthors{\output@author{#1 #2}{#4}}%
\ifx\@AIauthorsNames\@empty
\gdef\@AIauthorsNames{#1 #2}%
\typeout{Debug - @@author macro 3-> AIauth:\@AIauthorsNames}%
\else
\g@addto@macro\@AIauthorsNames{; #1 #2}%
\typeout{Debug - @@author macro 4-> AIauth:\@AIauthorsNames}%
\fi
}
\newcommand\output@author[2]
{%
% #1: <name> <surname>
% #2: affiliations
\def\baselinestretch{1}%
\refstepcounter{auth}% increment by 1
\notyetprocessedtrue
\typeout{Debug - @@author macro 2-> auth:#1}%
\authorsep#1\unskip\textsuperscript
{%
\@for\@@affmark:=#2\do
{%
%\typeout
% {%
% Debug - @@author macro -> auth:\theauth\ affmark:\@@affmark -
% author:#4
% }%
\edef\affnum
{\@ifundefined{X@\@@affmark}{affn?}{\AIrefMark{\@@affmark}}}%
\unskip\sep\affnum
\ifnotyetprocessed
\NewAIaffLabel{auth-\theauth}{+}%
\fi
\edef\cormark
{%
\@ifundefined{X@cor-\theauth}
{}%
{%
\if@showcorr
{,\AIrefMark{cor-\theauth}}%
\else
{}%
\fi
}%
}%
% this line was wrong, since it will add a extra comma in case of
% multiple affiliations:
% \unskip\sep\cormark\let\sep=,%
% now the issue is for the corresponding author with multiple
% affiliations
\unskip\cormark\let\sep=,%
\ifx\cormark\@empty
\else
% if the author is corresponding and is the first time we are here
\ifnotyetprocessed
% add the author name to the output
\g@addto@macro\@CorrespondingAuthorName{#1}%
\notyetprocessedfalse %if it is a subsequent round, do nothing
\fi
\fi
}%
}%
\gdef\authorsep{\unskip,\space}%
\global\let\sep\@empty
}
答案3
就像更新一样,我创建了一个可以工作的 MWE,它基于 @Ulrich Diez 的帮助。我感谢他的建议,使代码可以工作。
在接下来的几天里,我也会尝试@Skillmon 的解决方案,因为我发现它非常简洁(好吧,在我看来)。
如果有人发现我的代码中存在一些潜在问题,我会很高兴听到并修改它。
再次感谢你
费德里科
\documentclass{article}
\usepackage{keyval}
\makeatletter
\def\author{\@@author}
\def\@AIaffiliations{}
\def\@AIauthors{}
\def\@AIauthorsNames{}
\def\@AIauthorsNamesCitation{}
%some temporary variables for internal use
\let\authorsep\@empty
\let\sep\@empty
\let\@cormark\@empty
\def\CorrespondingAuthor{}
\def\@CorrespondingAuthorName{}
\def\@author@name{}
\def\@author@surname{}
\def\@authorshortname{}
\def\@tmpauthname{}
\define@key{authornames}{name}{%
\def\@author@name{#1}}
\define@key{authornames}{surname}{%
\def\@author@surname{#1}}
\define@key{authornames}{abbreviation}{%
\def\@authorshortname{#1}}
\newif\ifnotyetprocessed
\def\@@author[#1]#2{%
\setkeys{authornames}{#2}%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begingroup
% In LaTeX 2e \toks@ is a token register.
\toks@{\endgroup\processauthor{#1}}%
\protected@edef\tmpauthname{%
\the\toks@{\@author@name\space\@author@surname}{\@authorshortname}%
}%
\tmpauthname
}%
\newcounter{auth}
\long\def\processauthor#1#2#3{%
\g@addto@macro\@AIauthors{%
\def\baselinestretch{1}%
\refstepcounter{auth}% increment by 1
\notyetprocessedtrue
\authorsep#2\unskip
\textsuperscript{%
\@for\@@affmark:=#1\do{%
\edef\affnum{%
\@ifundefined{X@\@@affmark}{affn?}{\AIrefMark{\@@affmark}}%
}%
\unskip\sep\affnum%
\edef\cormark{%
\@ifundefined{X@cor-\theauth}{}{{}}%
}%
\unskip\cormark
\let\sep=,%
\ifx\cormark\@empty
\else
\ifnotyetprocessed
\g@addto@macro\@CorrespondingAuthorName{#2}
\notyetprocessedfalse
\fi
\fi
}%
}%
\gdef\authorsep{\unskip,\space}%
\global\let\sep\@empty
}%%
\ifx\@AIauthorsNames\@empty
\g@addto@macro\@AIauthorsNames{#2}%
\else
\g@addto@macro\@AIauthorsNames{;\space#2}%
\fi
\ifx\@AIauthorsNamesCitation\@empty
\g@addto@macro\@AIauthorsNamesCitation{#3}%
\else
\g@addto@macro\@AIauthorsNamesCitation{;\space#3}%
\fi
}%
\def\affiliation{\@@affiliation}
\newcounter{affn}
\renewcommand\theaffn{\arabic{affn}}
\long\def\@@affiliation[#1]#2{%
\g@addto@macro\@AIaffiliations{%
\def\baselinestretch{1}%
\refstepcounter{affn}%
\AIaffLabel{#1}
{\upshape\textsuperscript{\theaffn}}\space#2\par\vspace{1pt}}%
}
\def\AIaffLabel#1{\@bsphack\protected@write\@auxout{}%
{\string\NewAIaffLabel{#1}{\@currentlabel}}\@esphack}
\def\NewAIaffLabel#1#2{\expandafter\xdef\csname X@#1\endcsname{#2}}
\def\AIrefMark#1{\@ifundefined{X@#1}{0}{\csname X@#1\endcsname}%
}
\def\CorrespondingAuthorNumber#1{\NewAIaffLabel{cor-#1}{$\ast$}}
\newcounter{cnote}
\def\CorrespondingAuthorEmail#1{\refstepcounter{cnote}%
\AIaffLabel{#1}%
\g@addto@macro\CorrespondingAuthor{%
\@CorrespondingAuthorName, e-mail: #1}}
\def\@Citation{\@AIauthorsNamesCitation\space-\space\@title}
\renewcommand{\@maketitle}{%
\begingroup
\huge{\bfseries\@title}\par%
\vskip15pt%
\large{\bfseries\@AIauthors}\par%
\vskip14pt%
\itshape\small\@AIaffiliations\par%
{\bfseries Corresponding Author:}\space\CorrespondingAuthor\par
\vskip6pt%pt%
{\bfseries Citation:}\space\@Citation\par
\endgroup%%
}
\makeatother
\title{Test}
\author[a1,a2]{name=Tom,surname=Smith,abbreviation=T. Smith}%
\author[a2,a3]{name=Jane,surname=Doe,abbreviation=J. Doe}%
\author[a3,a4]{name=Alfred,surname=Einstein,abbreviation=A. Einstein}%
\affiliation[a1]{First Affiliation}
\affiliation[a2]{Second Affiliation}
\affiliation[a3]{Another Affiliation}
\affiliation[a4]{Just another Affiliation}
\CorrespondingAuthorNumber{3}
\CorrespondingAuthorEmail{[email protected]}
\begin{document}
\maketitle
\end{document}