我最近学习了如何创建一个宏来使用TeX 中的\afterassignment
and来迭代标记序列\let
。
目前,我正在尝试将其应用于标记化宏(其中“标记化”意味着围绕分隔字符/字符序列拆分字符序列);也就是说,stringTokenize
执行以下操作的算法:
character
在字符串中迭代- 如果
character
不是分隔符(例如,
),则添加character
到buffer
;如果character
是分隔符,则展开buffer
并对其执行某些操作(即传递给给定的宏)并清除buffer
。
然而这并不像我想象的那么简单,因为显然你不能将一个\let
命令的字符扩展为另一个命令(为了创建一个“缓冲区”结构),例如,
\edef\buffer{\buffer\character}
将(在n
迭代之后)扩展为
{\character\character(...)character}
是n
字符最后\let
出现的次数\character
。
我该怎么做呢?
以下是我目前所掌握的信息:
\documentclass{minimal}
\makeatletter
\def\structures@stringIterate#1#2{%
\newcount\@@index%
\def\@@stop{§}
\def\@@next{\afterassignment\@@each\let\@@current= }
\def\@@each{%
\ifx\@@current\@@stop%
\let\@@next=\relax
\else%
\advance\@@index1\relax
#2{\the\@@index}{\@@current}\let\@@next=\@@next
\fi%
\@@next % macro expansion must be delayed until the condition has been evaluated
}
\@@next#1\@@stop
}
\def\structures@stringTokenize#1#2#3{%
\newcount\@@index
\@@index 0\relax
\def\@@buffer{}
\def\@@test##1##2{%
\ifx#2##2
\advance\@@index 1\relax
#3{\the\@@index}{\@@buffer}
\def\@@buffer{} % reset the buffer
\else
\edef\@@buffer{\@@buffer##2} % gather character
\fi
}
\structures@stringIterate{#1}{\@@test}
}
\makeatother
\begin{document}
\makeatletter
\def\@@print#1#2{token (#1): ``#2''\par}
\structures@stringTokenize{Humpty Dumpty sat on a wall, Humpty Dumpty had a great fall, All the King's horses and all the King's men, Couldn't put Humpty together again.}{,}{\@@print}
\makeatother
\end{document}
答案1
在 TeX 中抓取带有分隔符的 token 的常用方法如下
\catcode`\@=11 %
\def\grabargs#1#2#3{%
\def\grabargs@aux##1#2{%
\ifx\relax##1\relax%
\else
#3{##1}\par
\expandafter\grabargs@aux
\fi
}%
\grabargs@aux#1#2\relax#2
}
\def\print#1{`{\tt #1}'}
\catcode`\@=12 %
\grabargs
{Humpty Dumpty sat on a wall, Humpty Dumpty had a great fall, All the King's horses and all the King's men, Couldn't put Humpty together again.}
{,}
{\print}
\bye
根据事情需要的复杂程度,我们可以使用更为奇特的方法来停止循环。
你不能在这里使用\let
(至少不能以任何特别简单的方式):这会创建隐式标记,正如您所发现的,可以扩展,例如\edef
。您可能希望做的是拾取空间标记:它们很难作为普通参数(#1
)来抓取,因此您可以\futurelet
在类似
\catcode`\@=11 %
\newcount\mycount
\def\grabargs#1#2#3{%
\def\grabbed@tokens{}%
\mycount\z@
\def\grabargs@auxii{%
\ifx\grab@token#2%
\grabargs@auxiv
\def\grabbed@tokens{}%
\expandafter\grabargs@auxv
\else
\ifx\grab@token\@sptoken
\expandafter\expandafter\expandafter\grabargs@space
\else
\ifx\grab@token\relax
\else
\expandafter\expandafter\expandafter\expandafter\expandafter
\expandafter\expandafter\grabargs@auxiii
\fi
\fi
\fi
}%
\def\grabargs@auxiv{%
#3{\the\mycount}\grabbed@tokens
\mycount\z@
}%
\grabargs@auxi#1#2\relax
}
\def\grabargs@auxi{%
\futurelet\grab@token\grabargs@auxii
}
\def\grabargs@auxiii#1{%
\edef\grabbed@tokens{%
\grabbed@tokens
#1%
}%
\advance\mycount\@ne
\grabargs@auxi
}
\def\grabargs@auxv#1{\grabargs@auxi}
\expandafter\def\expandafter\grabargs@space\space{%
\grabargs@auxiii\space
}
\def\:{\let\@sptoken= } \: %
\def\print#1#2{Tokens: #1 `{\tt #2}'\par}
\catcode`\@=12 %
\grabargs
{Humpty Dumpty sat on a wall, Humpty Dumpty had a great fall, All the King's horses and all the King's men, Couldn't put Humpty together again.}
{,}
{\print}
\bye
这里的想法\futurelet
和你的想法非常相似\afterassignment\@@each\let\@@current=
,只是没有从输入流中删除标记。我们使用抓取一个标记\futurelet
然后可以检查它。如果它是标记,我们可以打印当前抓取的标记(和计数),然后循环。如果它是结束标记,我们停止,如果它是“正常”标记,我们可以抓取参数并存储。如果它是一个空格,我们会执行一个特殊版本的“grab”宏来处理空格。我在上面没有添加的是处理括号组的代码:可以做到这一点,但方法取决于你想对这些事情做什么。
请注意,在上述两种实现中,我们都存储了标记,这可能意味着类别代码 6 标记(通常#
)存在问题,以及您希望如何管理换行符,ETC。这一切都可以解决,但如果没有更严格的规范,我就不管它了。另外,请注意,很容易陷入困境\futurelet
:\halign
例如,参见在哪里可以找到有关 \futurelet 的恶劣行为的记录?。