出于好奇,我想定义一个如下所示的 LaTeX 命令:
\newcommand{\trigger}{out1,out2,...,outn}
具有一个相当奇怪的功能(至少我在其他地方找不到类似的东西):
首先,参数的数量out1,out2,...
等都是变量。然后,每次\trigger
在文档内容中使用该命令时,都会产生不同的细绳打印:首先out1
,然后out2
等,最后outn
。outn
打印后,整个过程应重置,以便下一个细绳还会再来out1
的。
所以我遇到的第一个问题是创建一个可以读取逗号分隔列表的命令{}
,第二个问题是每次调用新定义的命令时如何仅打印其中一个。
答案1
之前的答案没有错,但这是另一个
\documentclass{article}
\newcommand{\triggerlist}{out1,out2,...,outn}
\def\trigger{\expandafter\xtrigger\triggerlist\xtrigger}
\def\xtrigger#1,#2\xtrigger{#1\def\triggerlist{#2,#1}}
\begin{document}
\trigger
\trigger
\trigger
\trigger
\trigger
\trigger
\trigger
\trigger
\end{document}
答案2
使用 expl3 非常简单:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\newsetcommand}{mm}
{
\seq_new:c { g_setcommand_ \cs_to_str:N #1 _seq }
\seq_gset_split:cnn { g_setcommand_ \cs_to_str:N #1 _seq } {,} {#2}
\cs_new:Npn #1
{
\seq_gpop_left:cN { g_setcommand_ \cs_to_str:N #1 _seq } \l_setcommand_temp_tl
\seq_gput_right:cV { g_setcommand_ \cs_to_str:N #1 _seq } \l_setcommand_temp_tl
\tl_use:N \l_setcommand_temp_tl
}
}
\tl_new:N \l_setcommand_temp_tl
\cs_generate_variant:Nn \seq_gset_split:Nnn { c }
\ExplSyntaxOff
\newsetcommand{\trigger}{out1,out2,out3,out4}
\begin{document}
\trigger
\trigger
\trigger
\trigger
\trigger
\end{document}
您会看到,\newsetcommand
定义了一个新的宏,该宏扩展到列表的连续元素,并且可以定义尽可能多的相同类型的命令。
输出如下:
怎么运行的
\newsetcommand{\trigger}{<list>}
定义
一个新的序列,名为
\g_setcommand_trigger_seq
,包含由逗号分隔的有序列表<list>
一个新的宏
\trigger
,每次调用时都会从关联序列中弹出最左边的元素,将其添加到序列的右侧并打印出来。
由于弹出的元素被添加回到序列的另一端,因此当打印最后一个原始元素时,下一个将再次成为第一个。
“经典”实现
\makeatletter
\newcommand{\newsetcommand}[2]{%
\toks@{}%
\@for\next:=#2\do{%
\toks@=\expandafter{\the\expandafter\toks@\expandafter{\next}}%
}
\expandafter\gdef\csname setcommand\string#1\expandafter\endcsname\expandafter{\the\toks@}%
\edef#1{\noexpand\@usecommand{\string#1}}%
}
\def\@usecommand#1{%
\expandafter\expandafter\expandafter\@@usecommand\csname setcommand#1\endcsname\@nil{#1}\@nil}
\def\@@usecommand#1#2\@nil#3\@nil{%
#1%
\expandafter\gdef\csname setcommand#3\endcsname{#2{#1}}%
}
\makeatother
语法与以前相同。
编辑
感谢大卫卡莱尔的回答,这可以简化:
\makeatletter
\newcommand{\newsetcommand}[2]{%
\global\@namedef{@setcommand\string#1}{#2}%
\edef#1{\noexpand\@usecommand{\string#1}}%
}
\def\@usecommand#1{%
\expandafter\expandafter\expandafter\@@usecommand\csname @setcommand#1\endcsname\@nil#1\@nil
}
\def\@@usecommand#1,#2\@nil#3\@nil{%
\global\@namedef{@setcommand#3}{#2,#1}%
}
\makeatother
答案3
我的卑微尝试etoolbox
,感谢Leo Liu 的回答。我使用\csdef
和\csuse
来存储和使用值,并\DeclareListParser
使用 来遍历逗号分隔列表中的项目。最后,该intcalc
包提供了模数功能。
\documentclass{article}
\usepackage{etoolbox}
\usepackage{intcalc}
\DeclareListParser*{\myforeach}{,}
\newcounter{helpercounter}
\newcommand{\savethistext}[1]{%
\stepcounter{helpercounter}%
\csdef{triggerval\thehelpercounter}{#1}}
\newcounter{progresscounter}
\newcommand{\trigger}[1]{%
\setcounter{progresscounter}{\intcalcMod{\value{progresscounter} + 1}{\value{helpercounter} + 1}}%
\csuse{triggerval\theprogresscounter}\par%
}
\myforeach{\savethistext}{out1, out2, out3, out4}
\begin{document}
\trigger
\trigger
\trigger
\trigger
\trigger
\trigger
\trigger
\trigger
\trigger
\trigger
\end{document}
答案4
以下是使用高级 ConTeXt 特征的实现:counters
和conversion
。
\defineconversion[trigger][out1, out2, out3, out4]
\definestructurecounter[trigger][numberconversion=trigger]
\setstructurecounter[trigger]{0}
\def\trigger%
{\incrementstructurecounter[trigger]%
\convertedstructurecounter[trigger]}
\starttext
\startlines
\trigger
\trigger
\trigger
\trigger
\trigger
\stoplines
\stoptext