我想知道如何存储变量以便可以对它们进行迭代。
假设我们在文档中有这样的输入:
\inputcommandname{first input}
\inputcommandname{second input}
\inputcommandname{third input}
稍后在文档中我们写入类似的内容\outputinreverseorder
并得到“输入 3 是‘第三个输入’。输入 2 是‘第二个输入’。输入 1 是‘第一个输入’。”写入类似的内容\outputinorder
会给出相反的顺序。
答案1
以下是一个实现expl3
:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
% first of all we define the user level commands
\NewDocumentCommand{\inputcommandname}{ m }{ \malmedal_input_add:n { #1 } }
\NewDocumentCommand{\outputinorder}{ }{ \malmedal_output_direct: }
\NewDocumentCommand{\outputinreverseorder}{ }{ \malmedal_output_reverse: }
% allocate some variables:
% 1. a sequence for global storage of the inputs;
% 2. a temporary sequence when we need to reverse;
% 3. a counter (integer variable)
\seq_new:N \g_malmedal_input_seq
\seq_new:N \l_malmedal_temp_seq
\int_new:N \l_malmedal_count_int
% store globally an input in the sequence
\cs_new:Npn \malmedal_input_add:n #1
{
\seq_gput_right:Nn \g_malmedal_input_seq { #1 }
}
% how to output in direct order; we simply do a mapping function calling
% \malmedal_print:n after incrementing the counter
\cs_new_protected:Npn \malmedal_output_direct:
{
\int_zero:N \l_malmedal_count_int
\seq_map_inline:Nn \g_malmedal_input_seq
{
\int_incr:N \l_malmedal_count_int
\malmedal_print:n { ##1 }
}
}
% how to output in reverse order; first we store a copy of the main
% sequence in the temporary one, that we reverse; then we set the
% integer variable to the length of the sequence; finally we map the
% temporary sequence, decrementing the counter after having printed
% its contents
\cs_new_protected:Npn \malmedal_output_reverse:
{
\seq_set_eq:NN \l_malmedal_temp_seq \g_malmedal_input_seq
\seq_reverse:N \l_malmedal_temp_seq
\int_set:Nn \l_malmedal_count_int { \seq_length:N \l_malmedal_temp_seq }
\seq_map_inline:Nn \l_malmedal_temp_seq
{
\malmedal_print:n { ##1 }
\int_decr:N \l_malmedal_count_int
}
}
% the printing macro; change here for adapting to your wishes
\cs_new:Npn \malmedal_print:n #1
{
Input ~ \int_to_arabic:n { \l_malmedal_count_int } ~ is ~ `#1' \par
}
\ExplSyntaxOff
\begin{document}
\inputcommandname{first input}
\inputcommandname{second input}
\inputcommandname{third input}
Here is direct order:
\outputinorder
\bigskip
Here is reverse order:
\outputinreverseorder
\end{document}
每个都\inputcommandname
将其参数全局添加到一个序列中,我们可以使用该序列按正序打印,也可以通过反转该序列以相反的顺序打印。
输出为
以下是直接顺序:
输入 1 是“第一个输入”
输入 2 是“第二个输入”
输入 3 是“第三个输入”以下是相反的顺序:
输入 3 是“第三个输入”
输入 2 是“第二个输入”
输入 1 是“第一个输入”
由于expl3
2012 年夏季对 所做的更改,上述代码\seq_length
应替换为\seq_count
\cs_new_protected:Npn \malmedal_output_reverse:
{
\seq_set_eq:NN \l_malmedal_temp_seq \g_malmedal_input_seq
\seq_reverse:N \l_malmedal_temp_seq
\int_set:Nn \l_malmedal_count_int { \seq_count:N \l_malmedal_temp_seq }
\seq_map_inline:Nn \l_malmedal_temp_seq
{
\malmedal_print:n { ##1 }
\int_decr:N \l_malmedal_count_int
}
}
答案2
现在有了 LuaTeX 解决方案。它具有 ConTeXt 风格,但也可以轻松适应 LaTeX。
\startluacode
userdata = { }
userdata.list = { }
local list = userdata.list
userdata.addtolist = function (item)
userdata.list[#list+1] = item
end
userdata.outputinorder = function ()
for i=1, #list do
context(list[i])
context.crlf()
end
end
userdata.outputinreverseorder = function ()
for i=#list, 1, -1 do
context(list[i])
context.crlf()
end
end
\stopluacode
\define[1]\inputcommandname{\ctxlua{userdata.addtolist("#1")}}
\define \outputinorder{\ctxlua{userdata.outputinorder()}}
\define \outputinreverseorder{\ctxlua{userdata.outputinreverseorder()}}
\starttext
\inputcommandname{first input}
\inputcommandname{second input}
\inputcommandname{third input}
\outputinorder
\blank
\outputinreverseorder
\stoptext
它只是将值存储在表中并按照要求的顺序输出表。
答案3
我想这和 Martin 的没什么不同,但这是一个简单的 TeX 解决方案
\def\stack{}
\def\stackcount{0}
\def\inputcommandname#1{{%
\let\xdo\relax
\count0=\stackcount\relax
\advance\count0 by 1
\xdef\stackcount{\the\count0}%
\toks0{\xdo{#1}}%
\toks2\expandafter{\stack}%
\xdef\stack{\the\toks2 \the\toks0 }}}
\def\outputinorder{{\count0=0
\def\xdo##1{\advance\count0 by 1
Input \the\count0 \space is: `##1'. }%
\stack}}
\def\outputinreverseorder{{%
\def\xdo##1##2\midstack##3\empty{%
##2\midstack\xdo{##1}##3\empty}%
\stack\midstack\empty}}
\def\midstack{%
\count0=\stackcount
\def\xdo##1{%
Input \the\count0 \space is: `##1'.
\advance\count0 by -1 }}
\inputcommandname{first input}
\inputcommandname{second input}
\inputcommandname{third input}
\outputinorder
\outputinreverseorder
\bye
答案4
这是 David Carlisle 解决方案的扩展/概括。(1) 可以使用命令 为不同目的创建多个堆栈;(2)可以使用多个命令名称为每个堆栈调用\inputcommandnames
命令\inputcommandnames
或;可以使用多个堆栈名称调用命令和;(4) 堆栈计数器仅在推送时调用,而不是弹出时调用。参见示例。\inputcommandname
\outputinnormalorder
\outputinreverseorder
\documentclass{article}
\usepackage{catoptions}
\makeatletter
\robust@def*\inputcommandnames{\cpt@testopt\@inputcommandnames{general}}
\newletcs\inputcommandname\inputcommandnames
% #1=stack name, #2=input name(s)
\robust@def*\@inputcommandnames[#1]#2{%
\ifblankFT{#1}{%
\@latex@info{No stack name given: using 'general' instead}%
}{%
% It is possible to iterate over stack names, but leave this for now:
% the use case isn't yet apparent.
\ifinsetTF,{#1}{%
\@latexerr{List '#1' not allowed}\@ehd
}{}%
}%
\ifcsndefTF{name@stack@#1}{}{\csn@def{name@stack@#1}{}}%
\ifcsndefTF{name@stackcount@#1}{}{\csn@def{name@stackcount@#1}{0}}%
\cptdocommalist{#2}{%
\csn@edef{name@stackcount@#1}%
{\the\numexpr\usename{name@stackcount@#1}+1}%
\csn@edef{name@stack@#1}{%
\expandcsnonce{name@stack@#1}\noexpand
\do{\usename{name@stackcount@#1}}{\unexpanded{##1}}%
}%
}%
}
\robust@def*\outputinnormalorder{\@outputcommandnames{normal}}
\newletcs\outputcommandnames\outputinnormalorder
\robust@def*\outputinreverseorder{\@outputcommandnames{reverse}}
% #1=listing/printing order, #2=stack name(s)
\robust@def*\@outputcommandnames#1#2{%
\cptdocommalist{#2}{%
\begingroup
\let\reserved@a\relax
\ifcsndefTF{name@stack@##1}{%
% If the stack is defined but it is currently empty, ignore it
% (but issue a warning). A 'verbose' option for triggering the
% warning will be useful here, but it hasn't been provided:
\ifcsnnullFT{name@stack@##1}{}{%
\@latex@info{Stack '##1' is empty}%
\def\reserved@a####1\endgroup{\endgroup}%
}%
}{%
\@latexerr{Stack '##1' is not defined}\@ehd
}%
\reserved@a
\xifstrcmpTF{#1}{normal}{%
\let\do\curroutputformat
\csname name@stack@##1\endcsname
}{%
\def\name@sentinel####1\name@stop{%
\let\do\curroutputformat####1%
}%
\def\do####1####2####3\name@sentinel####4\name@stop{%
####3\name@sentinel\do{####1}{####2}####4\name@stop
}%
\usename{name@stack@##1}\name@sentinel\name@stop
}%
\endgroup
}%
}
% \commandnameoutputformat<input no.><input item>
\robust@def*\commandnameoutputformat{%
% Skip any spurious spaces before opening brace:
\begingroup
\toks0{##1##2}%
\def\reserved@a{%
\expandafter\endgroup\expandafter\def\expandafter
\curroutputformat\the\toks\expandafter0\expandafter{\the\toks1}%
}%
\afterassignment\reserved@a\toks1=%
}
% If the stack isn't initialized after use, it will start building
% from the last count whenever it is called:
\robust@def*\initializecommandstacks#1{%
\cptdocommalist{#1}{%
\csn@def{name@stackcount@##1}{0}%
\csn@def{name@stack@##1}{}%
}%
}
\newletcs\initializecommandstack\initializecommandstacks
% Set empty stack 'xx' for warning later:
\csn@def{name@stack@xx}{}
\makeatother
% Examples:
\begin{document}
\inputcommandname{first input (general),second input (general),third input (general)}
\inputcommandname[stack-1]{First Input (stack-1),Second Input (stack-1),Third Input (stack-1)}
\commandnameoutputformat{Input number #1 is: #2.\par}
Normal order Output:\par
\outputinnormalorder{general,stack-1}
\par\medskip
Reverse order Output:\par
\outputinreverseorder{general,stack-1}
\par\medskip
Normal order Output:\par
\outputinnormalorder{stack-1}
\par\medskip
Reverse order Output:\par
\outputinreverseorder{stack-1}
% Issue a warning for empty stack 'xx' and continue:
\outputcommandnames{xx}
\outputinreverseorder{general}
\end{document}