将可选参数添加到 def

将可选参数添加到 def

因此,我正在寻找一种在乳胶中执行某些操作的方法,然后遇到了以下 MWE:

\documentclass{article}

\def\foo{1}
\def\bar#1 #2{%
    \expandafter\def\expandafter\foo\expandafter{\foo {}#1}
    \let\next\bar%
    \ifx#2\relax%
    \let\next\relax%
    \fi%
    \next#2%
}
\bar 2 3 4 \relax

\begin{document}
    \foo
\end{document}

它的作用基本上是从foo等于开始,1然后连接数字2,然后输出。341234

我明白最多这一切都在起作用,但我对两个不同的方面感到困惑(可能是同一个方面?),所以我想知道是否有人可以解释发生了什么。最后,我会提到我去做,希望能显示出我问这个问题的动机。

基本上,我的第一个困惑与以下内容有关:\def\bar#1 #2。通常我认为您必须添加一个可选参数,例如\def\bar[2]{#1 #2}或类似的东西,但这似乎在做一些不同的事情?#1 #2基本上是说2设置为#1然后有一个空格然后3 4设置为#2

我的第二个困惑是与术语有关\next。这也与循环有关,因为看起来不应该这样?我假设这是\next操作员在做这件事,所以我想知道这是如何工作的。

我提出这个问题的动机是我想做这样的事情,但要在某个数字之后停止。例如,我希望能够调用:\bar{4} 2 3 4 5 6 7\relax并让输出为1234(因此当它到达时它会停止4)。由于我不理解循环是如何发生的以及它\def是如何工作的,所以我不确定该怎么做。

提前致谢。

答案1

\next不是预定义运算符。它只是一个在循环过程中被重新定义多次的临时控制序列。

当被调用时会发生什么\bar(顺便说一下,这是一个坏名字,因为\bar 用于数学重音的预定义控制序列)?

它的定义有参数文本#1 #2,这意味着 TeX 将吸收所有内容,直到第一个空格标记,将其分配给参数#1,然后吸收进一步的标记或标记的括号列表,将其分配给参数#2

接下来执行一长串标记\expandafter。假设您有输入\bar 2 3 4 \relax

在这种情况下#12并且#23。那么 TeX 将会执行

\expandafter\def\expandafter\foo\expandafter{\foo {}2}

每个\expandafter标记被轮流触摸,最后一个标记被\foo扩展为当前含义,因此你得到

\def\foo{1{}2}

存储这样的定义,然后查看下一行,因此\next被赋予与 相同的含义\bar。然后#2将 和 进行比较\relax;在这种情况下,测试返回 false,因为#2是,因此跳过3之前的文本。它保持原样。\fi

\next#2

所以现在 TeX 面临着

\bar3 4 \relax

同样的事情现在发生了两次,所以我们得出

\def\foo{1{}2{}3{}4}

但现在有区别了,因为#2现在是\relax。所以\ifx测试返回 true,TeX 也返回\let\next\relax,所以最后一条指令变成

\relax\relax

循环结束。

这不是完成任务的特别好的方法,但它确实有效。顺便说一句,%行末缺少a \expandafter,并且接下来的两行中有多余的 a 。

如果您想更早地停止循环,则需要修改宏并引入计数器来索引运行次数。

也许您想要的是更加现代的循环。

\documentclass{article}

\ExplSyntaxOn

\seq_new:N \l__aram_addto_seq

\NewDocumentCommand{\aramaddto}{mom}
 {
  % first split the final argument at spaces (here ~ stands for a space)
  \seq_set_split:Nnn \l__aram_addto_seq { ~ } { #3 }
  \IfNoValueTF { #2 }
   {% no optional argument, add everything
    \tl_set:Nx #1 { \exp_not:V #1 \seq_use:Nn \l__aram_addto_seq { } }
   }
   {% optional argument, only add as many items as required
    \seq_map_indexed_inline:Nn \l__aram_addto_seq
     {% ##1 is the current index, ##2 is the item
      \int_compare:nTF { ##1 < #2 }
       {% still wanting to add
        \tl_put_right:Nn #1 { ##2 }
       }
       {% flush
        \seq_map_break:
       }
     }
   }
 }

\ExplSyntaxOff

\begin{document}

\newcommand{\fooA}{1}

\aramaddto\fooA{2 3 4}

Should print 1234: \fooA

\newcommand{\fooB}{1}

\aramaddto\fooB[4]{2 3 4 5 6}

Should print 1234: \fooB

\end{document}

在此处输入图片描述


在评论之后,这里是实现目标的一种方法,即输入一串数字并将它们全部相乘,当其中一个数字大于规定的上限时可以选择停止。

\documentclass{article}
\usepackage{xfp}

\ExplSyntaxOn

\NewDocumentCommand{\genfactorial}{om}
 {
  \IfNoValueTF { #1 }
   {
    \aram_genfactorial:en { \clist_item:nn { #2 } { -1 } } { #2 }
   }
   {
    \aram_genfactorial:nn { #1 } { #2 }
   }
 }

\seq_new:N \l__aram_genfactorial_seq

\cs_new_protected:Nn \aram_genfactorial:nn
 {
  \seq_clear:N \l__aram_genfactorial_seq
  \clist_map_inline:nn { #2 }
   {
    \int_compare:nTF { ##1 <= #1 }
     { \seq_put_right:Nn \l__aram_genfactorial_seq { ##1 } }
     { \clist_map_break: }
   }
  \fp_eval:n { \seq_use:Nn \l__aram_genfactorial_seq { * } }
 }
\cs_generate_variant:Nn \aram_genfactorial:nn { e }

\ExplSyntaxOff

\begin{document}

\genfactorial{1,2,3,4}

\genfactorial[45]{1,2,3,4,5,6,45,67,89}

\end{document}

使用逗号分隔列表似乎比用空格分隔项目更好。

在此处输入图片描述

更新

随着expl32021-05-07 的发布,由于新功能,我们可以使上述内容完全扩展\clist_map_tokens:nn

检查以0默认可选参数进行,假设输入由正数组成。

给定的输入被逐项映射并*<item>附加,但<item>如果可选参数为 0 或超出上限,则转换为 1。

\documentclass{article}
\usepackage{xfp}

\ExplSyntaxOn

\NewExpandableDocumentCommand{\genfactorial}{O{0}m}
 {
  \aram_genfactorial:nn { #1 } { #2 }
 }

\cs_new:Nn \aram_genfactorial:nn
 {
  \fp_eval:n 
   {
    1
    \clist_map_tokens:nn { #2 } { \__aram_genfactorial:nn { #1 } }
   }
 }
\cs_new:Nn \__aram_genfactorial:nn
 {
  \int_compare:nTF { #1 == 0 }
   {% no bound
    * #2
   }
   {% #1 is the upper bound
    \int_compare:nTF { #2 <= #1 } { * #2 } { * 1 }
   }
 }

\ExplSyntaxOff

\begin{document}

\genfactorial{1,2,3,4}

\genfactorial[4]{1,2,3,4,5,6,45,67,89}

\genfactorial[7]{1,2,3,4,5,6,45,67,89}

\genfactorial[45]{1,2,3,4,5,6,45,67,89}

\edef\test{\genfactorial{1,2,3,4,5,6,45,67,89}}\test

\end{document}

最后一行表明该功能确实是完全可扩展的。

在此处输入图片描述

答案2

循环更像是递归。它每次使用两个参数。

第一遍:它​​看到\bar 2 3;这里#1->2#2->3。数字 2 与 连接\foo\next设置为\bar(因为#2不是\relax)。

然后它被告知要处理\next #2与 相同的内容\bar 3。但缺少一个参数!因此,它会查找下一个参数,实际上您处理\bar 3 4。您再次运行相同的操作,但下一次,您最终会处理\bar 4 relax

这次,在进入之后4\foo在中\ifx你会看到#2\relax,所以\next被告知不再是\bar。因此递归停止。

相关内容