这是我的问题:我尝试编写一个带有一些参数(假设为 4 个)的命令,并且需要循环这些参数。像这样:
\documentclass[parskip=half]{scrartcl}
\usepackage{lmodern}
\usepackage[T1]{fontenc}
\usepackage{amssymb}
\usepackage{tikz}
\usepackage{hyperref}
\usepackage{multicol}
\renewcommand{\LayoutChoiceField}[2]{%
\leavevmode #2 #1%
}
\usepackage{enumerate}
\newcounter{itemp}
\newcommand\qqc[4]{%
\setcounter{itemp}{1}
\textbf{Question :}
\begin{multicols}{2}
\begin{enumerate}
\loop
{%
\item[{\ChoiceMenu[name=Q,radio,radiosymbol=\ding{52}]{}{=1}}]
{#{\the\value{itemp}}}\\
}
\stepcounter{itemp}
\ifnum \value{itemp} < 5
\repeat
\end{enumerate}
\end{multicols}
}%
\begin{document}
\qqc{I'm not Bill Gates, but you already know that}{I'm not John Gates}{Nor Joshua Gates}{Neither Kevin Gates. I don't rap.}
\end{document}
显然,问题出在 {#{\the\value{itemp}}} 上。我想获取 #1,然后是 #2、#3 和 #4,但不仅仅是字符串;我可以处理一些东西(例如打印 temp-th 参数)此外,我认为使用 etoolbox 并不合适,因为我们可以在参数中获得逗号和许多不同的符号,因此,列表没有适当的分隔符……如果您有任何想法,欢迎您。祝一切顺利,蒂埃里。
答案1
欢迎!我不会迭代参数,而是将列表传递给命令并迭代列表。如果您继续加载tikz
,您可以使用\foreach
它,但您不一定需要它,您也可以\@for
在这里使用。
\documentclass[parskip=half]{scrartcl}
\usepackage{lmodern}
\usepackage[T1]{fontenc}
\usepackage{amssymb}
\usepackage{hyperref}
\usepackage{multicol}
\renewcommand{\LayoutChoiceField}[2]{%
\leavevmode #2 #1%
}
\usepackage{enumerate}
\makeatletter
\newcommand\qqc[1]{%
\textbf{Question :}
\begin{multicols}{2}
\begin{enumerate}
\@for\temp:=#1\do{%
\item
[{\ChoiceMenu[name=Q,radio,radiosymbol=\ding{52}]{}{=1}}]{\temp}
}
\end{enumerate}
\end{multicols}
}%
\makeatother
\begin{document}
\begin{Form}
\qqc{{I'm not Bill Gates, but you already know that},%
{I'm not John Gates},%
{Nor Joshua Gates},%
{Neither Kevin Gates. I don't rap.}}
\end{Form}
\end{document}
我不得不将代码包装到\begin{Form}
并且\end{Form}
当然不知道你在想什么。
编辑:删除了不必要的计数器,正如 Ulrich Diez 指出的那样(谢谢!)。这是一个疏忽,答案的内容没有改变。
答案2
您使用 LaTeX 进行编程。LaTeX 基于 TeX,如 Donald Ervin Knuth 的 TeXbook 中所述。基本上,LaTeX 只是一组用 TeX 编写的宏,并打包为所谓的格式,以便在通过名为 latex/latex.exe/whatsoever 的可执行文件加载 TeX 程序时自动加载这些宏。TeXbook 中介绍的低级概念也适用于 LaTeX。因此,在以下解释中,我使用短语“TeX”的内容也适用于使用 LaTeX 进行编程。
将 TeX 宏视为在扩展过程中从标记流中删除的标记,并且还会触发从标记流中删除更多标记,然后将标记插入到标记流中。
“从 token 流中删除更多 token”是根据⟨参数文本⟩属于宏的⟨定义⟩。
“将标记插入到标记流中”是根据以下规则进行的:⟨平衡文本⟩也属于宏的⟨定义⟩。
插入到标记流中的标记形成“替换文本”。
句法#1
,#2
, ...,#9
用于表示宏的参数仅在定义宏/提供时可用⟨定义⟩宏。在扩展宏并随后处理形成扩展宏的替换文本的标记时,它不可用。
但是类似的做法#{\the\value{itemp}}
是尝试在扩展宏时以及之后处理该宏的替换文本时使此类语法可用。这在 TeX 中是不可能的。
因为在 TeX 中你不能直接通过宏处理超过 9 个参数,也就是说,如果没有像尾部递归地将同一个宏应用于多组参数这样的技巧,循环遍历参数的数量对我来说似乎没有必要。你只需这样做:
\documentclass[parskip=half]{scrartcl}
\usepackage{lmodern}
\usepackage[T1]{fontenc}
\usepackage{amssymb}
\usepackage{hyperref}
\usepackage{multicol}
\renewcommand{\LayoutChoiceField}[2]{%
\leavevmode #2 #1%
}
\usepackage{enumerate}
\newcommand\qqc[4]{%
\textbf{Question :}
\begin{multicols}{2}
\begin{enumerate}
\item
[{\ChoiceMenu[name=Q,radio,radiosymbol=\ding{52}]{}{=1}}]{#1}%
\item
[{\ChoiceMenu[name=Q,radio,radiosymbol=\ding{52}]{}{=1}}]{#2}%
\item
[{\ChoiceMenu[name=Q,radio,radiosymbol=\ding{52}]{}{=1}}]{#3}%
\item
[{\ChoiceMenu[name=Q,radio,radiosymbol=\ding{52}]{}{=1}}]{#4}%
\end{enumerate}
\end{multicols}
}%
\begin{document}
\begin{Form}
\qqc{I'm not Bill Gates, but you already know that}{I'm not John Gates}{Nor Joshua Gates}{Neither Kevin Gates. I don't rap.}
\end{Form}
\end{document}
如果您坚持使用一个循环,其中可以用序数来寻址参数,该序数表示它们在任意数量的参数排列中的位置,我可以提供一个宏:\ExtractKthArg{⟨integer K⟩}{⟨list of undelimited args⟩}
\makeatletter
%% Code for \ExtractKthArg
%%=============================================================================
%% Paraphernalia:
%% \UD@firstoftwo, \UD@secondoftwo, \UD@PassFirstToSecond,
%% \UD@CheckWhetherNull,
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
\UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
%%=============================================================================
%% Extract K-th inner undelimited argument:
%%
%% \ExtractKthArg{<integer K>}{<list of undelimited args>}
%%
%% In case there is no K-th argument in <list of undelimited args> :
%% Does not deliver any token.
%% In case there is a K-th argument in <list of undelimited args> :
%% Does deliver that K-th argument with one level of braces removed.
%%
%% Examples:
%%
%% \ExtractKthArg{0}{ABCDE} yields: <nothing>
%% \ExtractKthArg{3}{ABCDE} yields: C
%% \ExtractKthArg{3}{AB{CD}E} yields: CD
%% \ExtractKthArg{4}{{001}{002}{003}{004}{005}} yields: 004
%% \ExtractKthArg{6}{{001}{002}{003}} yields: <nothing>
%%
%%=============================================================================
\newcommand\ExtractKthArg[1]{%
\romannumeral0%
% #1: <integer number K>
\expandafter\UD@ExtractKthArgCheck
\expandafter{\romannumeral\number\number#1 000}%
}%
\newcommand\UD@ExtractKthArgCheck[2]{%
\UD@CheckWhetherNull{#1}{ }{%
\expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}#1}{#2}%
}%
}%
\newcommand\UD@ExtractKthArgLoop[2]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo#2{}.}{ }{%
\UD@CheckWhetherNull{#1}{%
\UD@ExtractFirstArgLoop{#2\UD@SelDOm}%
}{%
\expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}#2}%
{\expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}#1}}%
}%
}%
}%
\newcommand\UD@RemoveTillUD@SelDOm{}%
\long\def\UD@RemoveTillUD@SelDOm#1#2\UD@SelDOm{{#1}}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
{\UD@firstoftwo{\expandafter}{} \UD@secondoftwo{}#1}%
{\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%
%% End of code for \ExtractKthArg.
\makeatother
%%
%%-----------------------------------------------------------------------------
%%
\documentclass[parskip=half]{scrartcl}
\usepackage{lmodern}
\usepackage[T1]{fontenc}
\usepackage{amssymb}
\usepackage{hyperref}
\usepackage{multicol}
\renewcommand{\LayoutChoiceField}[2]{%
\leavevmode #2 #1%
}
\usepackage{enumerate}
\newcounter{itemp}
\newcommand\qqc[4]{%
\setcounter{itemp}{1}%
\textbf{Question :}
\begin{multicols}{2}%
\begin{enumerate}
\loop
\item
[{\ChoiceMenu[name=Q,radio,radiosymbol=\ding{52}]{}{=1}}]{\ExtractKthArg{\value{itemp}}{{#1}{#2}{#3}{#4}}}%
\stepcounter{itemp}%
\ifnum \value{itemp} < 5
\repeat
\end{enumerate}
\end{multicols}
}%
\begin{document}
\begin{Form}
\qqc{I'm not Bill Gates, but you already know that}{I'm not John Gates}{Nor Joshua Gates}{Neither Kevin Gates. I don't rap.}
\end{Form}
\end{document}
如果您只希望逐一处理所有参数,但不需要寻址/选择/挑选特定参数的可能性,则可以进行尾部递归:
\documentclass[parskip=half]{scrartcl}
\usepackage{lmodern}
\usepackage[T1]{fontenc}
\usepackage{amssymb}
\usepackage{hyperref}
\usepackage{multicol}
\renewcommand{\LayoutChoiceField}[2]{%
\leavevmode #2 #1%
}
\newcommand\RecursionStopper{\RecursionStopper}%
\makeatletter
\newcommand\tailrecursive[2]{%
% !!! #2 must not contain unbalanced \if../\else/\fi !!!
\ifx\RecursionStopper#2%
\expandafter\@gobble
\else
\expandafter\@firstofone
\fi
{#1{#2}\tailrecursive{#1}}%
}%
\makeatother
\usepackage{enumerate}
\newcommand\qqc[4]{%
\textbf{Question :}
\begin{multicols}{2}
\begin{enumerate}
\tailrecursive{%
\item
[{\ChoiceMenu[name=Q,radio,radiosymbol=\ding{52}]{}{=1}}]%
}{#1}{#2}{#3}{#4}{\RecursionStopper}%
\end{enumerate}
\end{multicols}
}%
\begin{document}
\begin{Form}
\qqc{I'm not Bill Gates, but you already know that}{I'm not John Gates}{Nor Joshua Gates}{Neither Kevin Gates. I don't rap.}
\end{Form}
\end{document}
在我对“在循环中用变量名定义新命令”这个问题的回答中我描述了宏的变体\DoWithEachElementOfArgumentList
。这可能会引起你的兴趣。
答案3
只需使用宏即可完成重复任务。我还修复了 的开头multicols
,这样您就不必担心“问题:”和问题之间出现分页符。
\documentclass[parskip=half]{scrartcl}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
\usepackage{amssymb}
\usepackage{tikz}
\usepackage{enumerate}
\usepackage{multicol}
\usepackage{hyperref} % should be last
\renewcommand{\LayoutChoiceField}[2]{\leavevmode #2 #1}
\newcommand{\doitem}[1]{%
\item[{\ChoiceMenu[name=Q,radio,radiosymbol=\ding{52}]{}{=1}}]{#1}
}
\newcommand\qqc[4]{%
\textbf{Question:}
\begin{multicols}{2}
\begin{enumerate}
\doitem{#1}\doitem{#2}\doitem{#3}\doitem{#4}
\end{enumerate}
\end{multicols}
}
\begin{document}
\begin{Form}
\qqc{I'm not Bill Gates, but you already know that}
{I'm not John Gates}
{Nor Joshua Gates}
{Neither Kevin Gates. I don't rap.}
\end{Form}
\end{document}
通过更改语法,您可以容纳您想要的项目数。语法与建议的相同另一个答案。
\documentclass[parskip=half]{scrartcl}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
\usepackage{amssymb}
\usepackage{tikz}
\usepackage{enumerate}
\usepackage{multicol}
\usepackage{xparse}
\usepackage{hyperref} % should be last
\renewcommand{\LayoutChoiceField}[2]{\leavevmode #2 #1}
\ExplSyntaxOn
\cs_new_protected:Nn \duncanidaho_qqc_item:n
{
\item[{\ChoiceMenu[name=Q,radio,radiosymbol=\ding{52}]{}{=1}}]{#1}
}
\NewDocumentCommand{\qqc}{m}
{
\begin{multicols}{2}[\textbf{Question:}]
\begin{enumerate}
\tl_map_function:nN { #1 } \duncanidaho_qqc_item:n
\end{enumerate}
\end{multicols}
}
\ExplSyntaxOff
\begin{document}
\begin{Form}
\qqc{
{I'm not Bill Gates, but you already know that}
{I'm not John Gates}
{Nor Joshua Gates}
{Neither Kevin Gates. I don't rap.}
}
\end{Form}
\begin{Form}
\qqc{
{I'm not Bill Gates, but you already know that}
{I'm not John Gates}
{Nor Joshua Gates}
{Neither Kevin Gates. I don't rap.}
{Whatever}
{Something else}
{Stop here}
{One more thing}
}
\end{Form}
\end{document}