如何在循环中索引命令的参数?

如何在循环中索引命令的参数?

这是我的问题:我尝试编写一个带有一些参数(假设为 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}

在此处输入图片描述

相关内容