我想要一个像这样工作的命令:
\dothings{Takes several words as argument}
并将其扩展为:
\@dothings{Takes} \@dothings{several} \@dothings{words}...
我知道如何定义一个简单的宏,将所有内容带到下一个空间,但我不知道如何使用它来应用它每个句子中的单词。有没有简单的方法可以做到这一点?
我也很好奇接下来的信作为一个论点,但我想这可能是一个单独的问题......
答案1
\documentclass{article}
\usepackage{xcolor}
\makeatletter
\def\dothings#1{\color{blue}\expandafter\dothings@i#1 \@nil}
\def\dothings@i#1 #2\@nil{%
\color{.!80}#1
\ifx\relax#2\relax\else\dothings@i#2\@nil\fi}
\makeatother
\begin{document}
\dothings{Takes several words as argument}
\end{document}
答案2
LaTeX3 现在提供
\seq_set_split:Nnn
<seq var>
{
<delimiter>
} {
<argument>
}
分裂<argument>
每次出现<delimiter>
(并从每个参数的两端删除空格),并将结果放入<seq var>
(LaTeX3 中的列表数据结构)。一旦您有了这个序列,您就可以将给定函数映射到其所有项目上。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\seq_new:N \l_foo_seq
\NewDocumentCommand{\dothings}{m}
{
\seq_set_split:Nnn \l_foo_seq {~} {#1}
\seq_map_inline:Nn \l_foo_seq { \fbox{##1} }
}
\ExplSyntaxOff
\begin{document}
\dothings{Takes several words as argument}
\end{document}
编辑:实际上你可以用 xparse 更快地完成它参数处理器(这里\SplitList
)。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand {\dothings}
{ > { \SplitList { ~ } } m }
{ \tl_map_inline:nn {#1} { \fbox{##1} } }
\ExplSyntaxOff
\begin{document}
\dothings{Takes several words as argument}
\end{document}
答案3
Herbert 已经展示了如何用空格分隔单词。这里有一个非常类似的解决方案,可以防止深度\ifx ... \fi
嵌套(当使用许多单词时,深度嵌套可能会产生错误):
\documentclass{minimal}
\begin{document}
\def\wordsloop#1{\wordsloopiter#1 \nil}
\def\wordsloopiter#1 #2\nil{%
\do{#1} %
\ifx&% #2 is empty, then & equeals &
\let\next\relax
\else
\def\next{\wordsloopiter#2\nil}% iterate
\fi
\next}
\def\do#1{(#1)}
\wordsloop{This is a sentence.}
% we have: (This) (is) (a) (sentence.)
\end{document}
然而,如果在论证中使用很多单词,它仍然会很慢。
#1
如果删除上述定义后面的空格,则可以通过类似的方法获得标记上的循环。但它可以更简单:
\documentclass{minimal}
\begin{document}
\def\tokensloop#1{\tokensloopiter#1\nil}
\def\tokensloopiter#1{%
\ifx\nil#1\else\do{#1}\expandafter\tokensloopiter\fi}
\def\do#1{(#1)}
\tokensloop{aa bb}
% we have (a)(a)(b)(b)
\end{document}
您也可以直接使用包\toksloop
中的命令etextools
。
请注意,空格将被忽略。
对于空格,可以使用 catcode 或其他技巧逐字读取字符。这更复杂。
例如,我们可以用来\obeyspaces
改变空格的 catcode:
\documentclass{minimal}
\begin{document}
\def\charsloop{\begingroup\obeyspaces\charsloophelper}
\def\charsloophelper#1{\charsloopiter#1\nil\endgroup}
\def\charsloopiter#1{%
\ifx\nil#1\else\do{#1}\expandafter\charsloopiter\fi}
\def\do#1{(#1)}
\charsloop{aa bb}
% we have (a)(a)( )( )(b)(b)
\end{document}
(换行符仍然会被忽略\obeyspaces
。)
答案4
ConTeXt 提供了一个宏\processisolatedwords
来实现这一点。例如,要在每个单词周围画一个框,你可以使用
\processisolatedwords {\input ward \relax} \inframed
在 MkIV 中,这个宏在 lua 中使用以下函数实现:
local function applytowords(list,what,nested)
local doaction = context[what or "ruledhbox"]
local noaction = context
local current = checkedlist(list)
local start
while current do
local id = current.id
if id == glue_code then
if start then
doaction(copynodelist(start,current))
start = nil
end
noaction(copynode(current))
elseif nested and (id == hlist_code or id == vlist_code) then
context.hbox()
context.bgroup()
applytowords(current.list,what,nested)
context.egroup()
elseif not start then
start = current
end
current = current.next
end
if start then
doaction(copynodelist(start))
end
end
也可以看看:
\applytowords
\processwords
和\applytosplitstringword