\firstbanner
我正在尝试使用 LaTeX 存储和更新三个横幅、、\secondbanner
和的值\thirdbanner
,使用\addbanner
命令进行更新\firstbanner
并将旧\firstbanner
值放入\secondbanner
,将旧值\secondbanner
放入\thirdbanner
。但是,当我\addbanner
使用新值调用时,所有三个横幅最终都具有相同的值。这是我使用的代码:
\newcommand{\thirdbanner}{three}
\newcommand{\secondbanner}{two}
\newcommand{\firstbanner}{one}
\newcommand{\printbanners}{
Third banner :\thirdbanner \\
Second banner :\secondbanner \\
First banner :\firstbanner \\
}
\newcommand{\addbanner}[1]{
\\ Adding #1: \\ \printbanners \\
\renewcommand{\thirdbanner}{\secondbanner}
renew third : \printbanners \\
\renewcommand{\secondbanner}{\firstbanner}
renew seconf: \printbanners \\
\renewcommand{\firstbanner}{hello}
renew first \printbanners
}
\addbanner{hello}
上面的输出:
答案1
当你这样做时,\renewcommand{\thirdbanner}{\secondbanner}
你并没有定义\thirdbanner
拥有的当前值\secondbanner
,但是你正在定义\thirdbanner
扩展为\secondbanner
。
您需要\secondbanner
使用类似以下方式访问的当前值
\expandafter\renewcommand\expandafter\thirdbanner\expandafter{\secondbanner}
或者
\edef\thirdbanner{\unexpanded\expandafter{\secondbanner}}
我建议采用不同的策略:使用一个序列,您可以在其中的任一端添加项目,在本例中是在左侧添加。
\documentclass{article}
\ExplSyntaxOn
\seq_new:N \g_egreg_banner_seq
\NewDocumentCommand{\addbanner}{m}
{
\seq_gput_left:Nn \g_egreg_banner_seq { #1 }
}
\NewExpandableDocumentCommand{\getbanner}{m}
{
\seq_item:Nn \g_egreg_banner_seq { #1 }
}
\ExplSyntaxOff
\newcommand{\printbanners}{%
Third banner: \getbanner{3}\par
Second banner: \getbanner{2}\par
First banner: \getbanner{1}\par
}
\begin{document}
\addbanner{three}
\addbanner{two}
\addbanner{one}
\printbanners
\bigskip
\addbanner{hello}
\printbanners
\bigskip
\addbanner{world}
\printbanners
\end{document}
作为奖励,您可以访问全部您定义的横幅。
这是一个遗留的实现,但缺点是只保留了前三项。
\documentclass{article}
\makeatletter
% user level commands
\newcommand{\addbanner}[1]{%
\expandafter\banner@add\expandafter{\banner@container}{#1}%
}
\newcommand{\firstbanner}{%
\expandafter\banner@first\banner@container\@nil
}
\newcommand{\secondbanner}{%
\expandafter\banner@second\banner@container\@nil
}
\newcommand{\thirdbanner}{%
\expandafter\banner@third\banner@container\@nil
}
% internal macros
\newcommand{\banner@container}{{}{}{}}% ensure at least three items
\newcommand\banner@add[2]{\banner@add@aux{#2}#1\@nil}
\def\banner@add@aux#1#2#3#4\@nil{%
\gdef\banner@container{{#1}{#2}{#3}}%
}
\def\banner@first#1#2#3#4\@nil{#1}
\def\banner@second#1#2#3#4\@nil{#2}
\def\banner@third#1#2#3#4\@nil{#3}
\makeatother
\newcommand{\printbanners}{%
Third banner: \thirdbanner\par
Second banner: \secondbanner\par
First banner: \firstbanner\par
}
\begin{document}
\addbanner{three}
\addbanner{two}
\addbanner{one}
\printbanners
\bigskip
\addbanner{hello}
\printbanners
\bigskip
\addbanner{world}
\printbanners
\end{document}
答案2
欢迎来到宏的“奇妙”世界,它定义“文本”扩展,即替换。
作为初学者,我也习惯使用“常见”编程语言(如 C、C++、C#、Pascal)或脚本语言(如 Python 和 Perl)进行编程。而当第一次接触宏语言时,这种编程语言往往……神秘而出乎意料 :-)。
结果出乎意料,因为人们在做类似的事情时会像在 C 语言中一样以值分配的方式思考
int i = 1;
int j = i;
i = 2;
并且人们期望i
现在有一个值2
,而j
有一个值1
。定义宏不会将右侧的求值赋给宏,而是将标记列表赋值为替代品到宏。当“调用”宏时,宏标记将被上一个宏定义中有效的标记列表替换。在处理宏时,人们可能会想到“惰性求值”之类的东西,因为在定义宏时,替换文本不会被求值/扩展。
因此之后
\newcommand{\i}{1}
\newcommand{\j}{\i}
\renewcommand{\i}{2}
这两个宏\i
和\j
具有以下替换标记列表: 在第一个之后\i
被替换,最后在之后被替换,而将被替换,之后不会更改,仍然将被替换1
\newcommand
2
\renewcommand
\j
\i
\i
和不是1
正如预期的那样,通过标记列表。
(要查看宏的当前定义,您可以使用原语\show\i
和\show\j
,它将输出指定的替换作为标记列表。如果您使用 TeX GUI,则必须检查日志输出。)
答案3
您需要将一个命令的定义复制到另一个命令中,而不是重新定义它来显示该命令。这通常通过\let
或来实现\NewCommandCopy
。
\documentclass{article}
\newcommand{\thirdbanner}{three}
\newcommand{\secondbanner}{two}
\newcommand{\firstbanner}{one}
\newcommand{\printbanners}{%
Third banner: \thirdbanner \\
Second banner: \secondbanner \\
First banner: \firstbanner
}
\newcommand{\addbanner}[1]{%
Adding #1: \\
\let\thirdbanner\secondbanner
renew third: \thirdbanner \\
\let\secondbanner\firstbanner
renew second: \secondbanner \\
\renewcommand{\firstbanner}{#1}%
renew first: \firstbanner
}
\setlength{\parindent}{0pt}% Just for this example
\begin{document}
\printbanners
\bigskip
\addbanner{how}
\medskip
\addbanner{now}
\medskip
\addbanner{brown}
\medskip
\addbanner{cow}
\end{document}