我想定义一个新命令,它是 \csvsimple 包中已定义的命令的组合。因此我编写了以下代码,它正确地满足了我的目的:
\newcommand{\myfirstcommand}{\csvcoli & \csvcolii & \csvcoliii & \csvcoliv & \csvcolv} % the \csvcoli command refers to the 1st column of a csv table, \csvcolii to the 2nd column, and so on.
\newcommand{\mysecondcommand}{\myfirstcommand}
但是,我想将其写成\myfirstcommand
for 循环,而不必手动写下所有 5 列的 \csvcol 命令。因此,我尝试了以下方法:
\newcommand{\myfirstcommand}{
\foreach \i in {1,...,5}{
\csname csvcol\romannumeral\i\endcsname
\ifnum\i < 5
\&
\fi
}
}
\newcommand{\mysecondcommand}{\myfirstcommand}
但是,后一个代码的结果实际上并不等同于前一个代码。我该如何解决这个问题?也许我必须使用\noexpand
或\expandafter
..?
答案1
您可以在 TeX 原始级别使用简单的宏来完成此操作:
\def\addto#1#2{\expandafter\def\expandafter#1\expandafter{#1#2}}
\newcount\num
\def\myfirstcommand{}\num=0
\loop
\advance\num by1
\ifnum\num>1 \addto\myfirstcommand{&}\fi
\expandafter\addto\expandafter\myfirstcommand\csname csvcol\romannumeral\num\endcsname
\ifnum\num<5 \repeat
%% Test:
\message{\meaning\myfirstcommand}
如果要设置为由给定“列”数限制的\mycommand
列表等,则可以在表前和表内使用以下宏。\csvcoli & \csvcolii
\setmycommand{number}
\mycommand
\def\addto#1#2{\expandafter\def\expandafter#1\expandafter{#1#2}}
\newcount\num
\def\setmycommand#1{%
\def\mycommand{}\num=0
\loop
\advance\num by1
\ifnum\num>1 \addto\myfirstcommand{&}\fi
\expandafter\addto\expandafter\mycommand\csname csvcol\romannumeral\num\endcsname
\ifnum\num<#1 \repeat
}
%% Test:
... before the table: \setmycommand{7}
... inside the table use \mycommand.
答案2
您可以使用不同类型的循环来实现这一点。但不能使用\foreach
,因为有几个原因与可扩展性和分组有关。
\documentclass{article}
\ExplSyntaxOn
\NewDocumentCommand{\definecsvcommand}{mm}
{% #1 = command to be defined
% #2 = number of columns (should be ≥ 1)
% we build the appropriate token list
\tl_set:Nn \l_tmpa_tl { \csvcoli }
% the loop will step from 2 to #2 where we also add &
\int_step_inline:nnn { 2 } { #2 }
{
\tl_put_right:Ne \l_tmpa_tl { & \exp_not:c { csvcol \int_to_roman:n { ##1 } } }
}
% build the temporary command
\cs_set:NV \__mattiap_csv_command: \l_tmpa_tl
% finish up
\cs_new_eq:NN #1 \__mattiap_csv_command:
}
\cs_generate_variant:Nn \cs_set:Nn { NV }
\cs_new:Nn \__mattiap_csv_command: {} % initialize
\ExplSyntaxOff
\definecsvcommand{\mycommand}{5}
\ShowCommand{\mycommand}
\stop
控制台将显示该命令按预期定义:
> \mycommand=\long macro:
->\csvcoli &\csvcolii &\csvcoliii &\csvcoliv &\csvcolv .
这个想法是建立适当的令牌列表:
\exp_not:c { csvcol \int_to_roman:n { ##1 } }
控制序列\csvcoli
等\csvcolii
被创建,但不再在请求的扩展中进一步扩展e
。之后,利用\cs_new:Nn
(实际上是其变体) 定义临时命令,并使请求的命令等于临时命令。
如果只需要一个命令,代码非常简单:
\ExplSyntaxOn
\tl_set:Nn \l_tmpa_tl { \csvcoli }
\int_step_inline:nnn { 2 } { 5 }
{
\tl_put_right:Ne \l_tmpa_tl { & \exp_not:c { csvcol \int_to_roman:n { #1 } } }
}
\exp_args:NNV \cs_new:Npn \mycommand \l_tmpa_tl
\ExplSyntaxOff
哪个可能简化为
\ExplSyntaxOn
\tl_set:Nn \mycommand { \csvcoli }
\int_step_inline:nnn { 2 } { 5 }
{
\tl_put_right:Ne \mycommand { & \exp_not:c { csvcol \int_to_roman:n { #1 } } }
}
\ExplSyntaxOff
并且可以与其他答案中的代码进行比较,就简洁性而言。为什么可能? 因为expl3
最好保持标记列表变量和宏/函数之间的区别。
我们要定义的是执行某些操作的命令,因此它不是某些标记的容器。归根结底,两者都是 TeX 宏,但这并不重要。
进一步注意:使用扩展分配\tl_put_right:Ne
避免执行两个步骤(首先添加&
然后\csvcol<suffix>
),而\exp_not:c
清楚地执行“形成控制序列名称,但不要进一步扩展”。
答案3
您可以使用\romannumeral
尾递归循环来驱动扩展。
如果要在表格环境/对齐内执行循环,则需要确保将用户提供内容的宏参数嵌套在花括号之间进行“屏蔽”,直到结果传递为止。
否则,诸如表示数字 38 的字母常量之类的东西`&
可能会被错误地用作表格单元格分隔符,进而会破坏\romannumeral
驱动的扩展链。
例如,查看错误消息
\documentclass{article}
\begin{document}
\def\foo#1{#1}%
\begin{tabular}{ll}
arabic&roman\\
text \number38 &text \romannumeral38\\
text \expandafter\foo\expandafter{\number`&}&text \expandafter\foo\expandafter{\romannumeral`&}%
%\\text \number`&&text \romannumeral`&
\end{tabular}
\end{document}
取消注释之前的行时\end{tabular}
。
以下是代码:
\makeatletter
% \romannumeral\commandloop{<lower bound>}%
% {<result gathered so far>}%
% {<upper bound>}%
% {<prefix>}{<postfix>}{<separator>}{<next separator>}
\@ifdefinable\stopromannumeral{\chardef\stopromannumeral=`\^^00 }%
\newcommand\exchange[2]{#2#1}%
\newcommand\commandloop[7]{%
\expandafter\@firstofone\expandafter{%
\ifnum\numexpr(#3)\relax<\numexpr(#1)\relax
\expandafter\@firstoftwo\else
}\expandafter\@secondoftwo\fi
{\stopromannumeral#2}{%
\expandafter\commandloop\expandafter{%
\the\numexpr((#1)+1)\expandafter\relax\expandafter
}\expandafter{%
\romannumeral
\expandafter\exchange\expandafter{%
\csname#4\romannumeral\numexpr(#1)\relax#5\endcsname
}{\stopromannumeral#2#6}%
}{#3}{#4}{#5}{#7}{#7}%
}%
}%
\makeatother
%--------------------------------------------------------------
\newcommand{\myfirstcommand}{%
\romannumeral\commandloop{1}{}{5}{csvcol}{}{}{&}%
}%
%--------------------------------------------------------------
\newcommand{\mysecondcommand}{\myfirstcommand}
%--------------------------------------------------------------
\expandafter\newcommand\expandafter\mythirdcommand\expandafter{%
\romannumeral\commandloop{1}{}{5}{csvcol}{}{}{&}%
}%
%--------------------------------------------------------------
\newcommand\PassFirstToSecond[2]{#2{#1}}%
\expandafter\PassFirstToSecond\expandafter{%
\romannumeral\commandloop{1}{}{5}{csvcol}{}{}{&}%
}{\newcommand\myfourthcommand}%
%--------------------------------------------------------------
\show\myfirstcommand
\show\mysecondcommand
\show\mythirdcommand
\show\myfourthcommand
\stop