TeX 用户可以简单地创建带有由任意标记序列分隔的参数的宏。例如
\def\macro #1delimiter{...#1...}
是否可以创建一个具有多个分隔符的参数的宏?我的意思是这样的:
\mydef\macro #1[first or second]{...#1...}
\macro text first % -> ...text ... (delimiter is first)
\macro text second % -> ...text ... (delimiter is second)
参数文本应从分隔符列表中扫描到第一个分隔符。无需再扫描。我知道您可以在此之后设置一个固定分隔符(例如 \par),然后使用这个较长的参数进行处理并从中读取单词“first”或“second”。但这种方法读取的输入数据比我们需要的要多。进行了不必要的标记化,也许这里是不平衡的文本等。
答案1
您可以\seplist
完全按照问题中描述的目的使用我的宏。该宏\seplist
具有以下语法:
\def\yourmacro#1{... macro with normal #1 without separator}
\seplist{list of separators}\yourmacro parameter text
例如:
\def\macro#1{parameter is: "#1"}
\def\separed{\seplist{{sepA}{SepB}{SEPC}}}
\separed\macro text separated by sepA % sepA is separator
\separed\macro text separated by SepB % SepB is separator
\separed\macro text separated by SEPC % SEPC is separator
实际使用的分隔符被全局存储在\sepused
宏中。宏程序员可以使用它。
输入流读取到列出的任何分隔符的第一个实例,不再有。分隔符列表包括括号中的分隔符。如果只有一个标记分隔符,则可以省略括号。示例:
\seplist{0123456789}\macro text to the first decimal digit 7
分隔符可以由除{
}
和之外的任何标记组成#
(更确切地说,这些字符的类别起着作用)。这与\def
使用带有单个分隔符的原始字符时相同。参数文本可以包含这些字符,但必须始终保持平衡。因此,隐藏在括号中的分隔符将被忽略。此行为与普通分隔参数类似。示例:
\seplist{0123456789}\macro this text {1234} is separated by five: 5
\seplist{\undefined \defined}\macro this text ends by \undefined or \defined
\seplist{{\undefined\defined}\par}\macro this text ends by \undefined\defined or by \par
如果有多个分隔符,它们都匹配相同的文本,则较长的分隔符获胜。示例:
\def\m#1{\message{param: "#1", separator: "\sepused"}}
\seplist{{BC}{ABC}}\m ahaABC % -> param: "aha", separator: "ABC"
如果您的\macro
定义为\long
则\par
可以扫描到参数中。警告:如果您的\macro
未定义为\long
则参数扫描不会停止在\par
!您必须将 添加\par
到分隔符列表中。示例:
\def\parinmacro{\par}
\def\thismacro{\ifx\sepused\parinmacro \message{something wrong}\fi ...}
\seplist{{ab}{cd}\par}\thismacro This text skips the hidden {ab} and {cd}
and it stops at the end of the paragraph.
您可以使用\sepdef
与您的要求类似的语法定义宏:
\def\sepdef #1#2[#3]{\def#1{\seplist{#3}{\csname.\string#1\endcsname}}%
\long\expandafter\def\csname.\string#1\endcsname ##1}
\sepdef\test #1[{first}{second}]{Parameter is: "#1", separator is "\sepused".}
\test text separated by first
\test text separated by second
宏的实现\seplist
如下。它仅使用原语和基本宏。它应该适用于任何 TeX 格式。
\long\def\addto#1#2{\expandafter\def\expandafter#1\expandafter{#1#2}}
\newtoks\seplistT
\long\def\seplistD#1{%
\seplistS##2\seplistE{\def\tmpa{##1}\def\tmpb{##2}\seplistE}%
\def\tmpb{\tmpa #1}\expandafter\tmpb \tmp\seplistD\seplistE
}
\long\def\seplistE#1{%
\ifx\tmpa\empty
\seplistS\seplistD{\def\tmpb{##1}}\expandafter\tmpa\tmpb
\ifx\tmpb\empty \seplistQ{#1}%
\else \expandafter\addto\expandafter\seplistLx
\expandafter {\expandafter\seplistD\expandafter{\tmpb}{#1}}%
\fi\fi
}
\def\seplistS{\long\expandafter\def\expandafter\tmpa\expandafter##\expandafter1\tmp}
\long\def\seplistQ#1#2\seplistA{\fi\fi\gdef\sepused{#1}\seplistZ}
\long\def\seplist#1#2{\begingroup
\toks0={#2}\let\bgroup=\relax \let\egroup=\relax
\def\seplistL{}\def\seplistLx{}\seplistI#1{}\gdef\sepused{}%
\ifx\seplistL\empty \expandafter\endgroup \the\toks0\else
\seplistT={}\expandafter\seplistA\fi
}
\def\seplistA{\futurelet\tmp\seplistB}
\def\seplistB{\let\next=\seplistP
\expandafter\ifx\space\tmp \let\next=\seplistC \let\nexxt=\seplistM \fi
\ifx##\tmp \let\next=\seplistC \let\nexxt=\seplistH \fi
\ifx{\tmp \let\next=\seplistG \fi
\ifx}\tmp \let\next=\seplistC \let\nexxt=\seplistF \fi
\next
}
\def\seplistC{\afterassignment\nexxt \let\next= }
\long\def\seplistP#1{\seplistX#1\def\tmp{#1}\seplistN}
\def\seplistM{\seplistX{ }\def\tmp{ }\seplistN}
\def\seplistH{\seplistX{##}\def\seplistLx{}\seplistA}
\def\seplistN{\edef\seplistLx{\expandafter}\seplistLx \seplistL \seplistA}
\long\def\seplistG#1{\def\seplistLx{}\seplistX{{#1}}\seplistA}
\def\seplistF{\seplistT\expandafter{\expandafter{\the\seplistT}}\seplistZ}
\long\def\seplistX#1{\seplistT\expandafter{\the\seplistT#1}}
\def\seplistZ{\let\tmp=\sepused
\expandafter\seplistS\expandafter{\the\toks0{##1}}%
\expandafter\expandafter\expandafter\endgroup\expandafter\tmpa\the\seplistT
}
\long\def\seplistI#1{\ifx\seplistI#1\seplistI\else
\addto\seplistL{\seplistD{#1}{#1}}\expandafter\seplistI \fi
}
对实施的评论
我们以类似方式读取参数 token-per-token此主题并将这些标记存储在\seplistT
标记列表中。内部宏\seplistL
包含以下形式的分隔符列表:
\seplistD{sepA}{sepA}\seplistD{sepB}{sepB}\seplistD{sepC}{sepC}...
我们将已经读取的标记存储到\tmp
并运行\seplistL
。更确切地说:在开始时,临时文件\seplistLx
为空。对于每个读取的标记,我们将
\seplistLx
和扩展\seplistL
至输入流,并在执行之前重置\def\seplistLx{}
。现在,执行输入流,即,
\seplistD
为每个分隔符处理宏。的任务
\seplistD{sepA}{sepA}
如下:测试是否\tmp
等于其第一个参数的第一个标记(s
在此示例中为)。如果为真,则\seplistD
(使用\seplistE
)将文本\seplistD{epA}{sepA}
(从第一个参数中删除第一个标记) 添加到\seplistLx
将为下一个标记执行的临时列表中。如果\tmp
不等于第一个参数的第一个标记,\seplistD
则不执行任何操作。
例如,中的下一个读取标记\tmp
是e
。然后\seplistD{epA}{sepA}
将 保存到\seplistD{pA}{sepA}
,\seplistLx
因为第一个字母是e
。如果 中的下一个标记\tmp
是p
,\seplistD{A}{sepA}
则将 存储到\seplistLx
。最后,如果 中的下一个标记\tmp
是A
,则不
\seplistD{A}{sepA}
存储\seplistD{}{sepA}
,但它判定找到了分隔符,因为第一个参数为空。它将 定义\sepused
为其第二个参数( ),并通过 加 来sepA
结束此游戏。如果 中的最后一个标记不是,则不执行任何操作,并且链被断开,因为在每个步骤中都设置为空。可以构建新链,因为仍然包含在 中,在计算过程中不会更改。\seplistQ
\seplistZ
\tmp
A
\seplistD{A}{sepA}
\seplistLx
\seplistD{sepA}{sepA}
\seplistL
TeX 中的宏编程非常漂亮,但它与“普通”编程所使用的传统技术不同。我们在这里利用了代码可以创建代码的事实,即数据和代码并非严格分离。