我想对某个空格分隔的列表执行特定命令作为参数。然后对同一列表执行相同的命令(不包括最后一个元素),直到列表为空。作为附加点,将剪切出的元素存储到单独的列表中。
我目前拥有的:
\documentclass{standalone}
\makeatletter
\def\split#1{
\typeout{split:#1}
\def\init{}
\def\last{}
\@split#1 \@empty
}
\def\@split#1 #2{%
\typeout{1:#1}
\typeout{2:#2}
\ifx #2\@empty
\def\last{#1}
\else
\ifx\init\@empty
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\init
\expandafter\expandafter\expandafter{\expandafter\init#1}
\else
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\init
\expandafter\expandafter\expandafter{\expandafter\init\space#1}
\fi
\expandafter\@split
\fi
#2%
}
\def\writer#1{%
\typeout{writer:#1}
\split{#1}
\typeout{writer-init:\init}
\typeout{writer-last:\last}
\ifx \init\@empty\else\writer{\init}\fi
}
\makeatother
\begin{document}
\writer{Takes several words as argument}
\end{document}
问题是第二次迭代时@split
接收空参数。我使用\typeout{}
命令查看每个命令接收的参数。控制台输出如下:
writer:Takes several words as argument
split:Takes several words as argument
1:Takes
2:s
1:several
2:w
1:words
2:a
1:as
2:a
1:argument
2:
writer-init:Takes several words as
writer-last:argument
writer:Takes several words as
split:Takes several words as
1:
2:
writer-init:
writer-last:
如您所见,\split
它本身接收了正确的数据,但\@split
命令无法解析参数。我确信我在将数据传递给时犯了错误\@split
,但不知道如何修复它。
我非常感谢任何建议。
答案1
您漏掉了几个\expandafter
:
\ifx\init\@empty\else\expandafter\writer\expandafter{\init}\fi
如果我更改相应的行,我会在终端上得到以下输出:
writer:Takes several words as argument
split:Takes several words as argument
1:Takes
2:s
1:several
2:w
1:words
2:a
1:as
2:a
1:argument
2:
writer-init:Takes several words as
writer-last:argument
writer:Takes several words as
split:Takes several words as
1:Takes
2:s
1:several
2:w
1:words
2:a
1:as
2:
writer-init:Takes several words
writer-last:as
writer:Takes several words
split:Takes several words
1:Takes
2:s
1:several
2:w
1:words
2:
writer-init:Takes several
writer-last:words
writer:Takes several
split:Takes several
1:Takes
2:s
1:several
2:
writer-init:Takes
writer-last:several
writer:Takes
split:Takes
1:Takes
2:
writer-init:
writer-last:Takes
这里有一个可能更好的实现,它利用标记寄存器而不是那些复杂的\expandafter
's 链,或者,作为替代方案,\unexpanded
它来自 e-TeX。请注意,所有最近的 TeX 发行版在调用 LaTeX 时都使用 e-TeX。
\documentclass{article}
\makeatletter
\def\split#1{%
\typeout{split:#1}%
\def\init{}%
\def\last{}%
\@split#1 \@empty
}
\def\@split#1 #2{%
\typeout{1:#1}%
\typeout{2:\noexpand#2}% \noexpand for showing also `\@empty`
\ifx #2\@empty
\def\last{#1}%
\else
\ifx\init\@empty
\def\init{#1}%
\else
% with e-TeX use the following line
\edef\init{\unexpanded\expandafter{\init}\space\unexpanded{#1}}%
% without e-TeX use the following two lines
%\toks@=\expandafter\expandafter\expandafter{\expandafter\init\space#1}%
%\edef\init{\the\toks@}%
\fi
\expandafter\@split
\fi
#2%
}
\def\writer#1{%
\typeout{writer:#1}%
\split{#1}%
\typeout{writer-init:\init}%
\typeout{writer-last:\last}%
\ifx\init\@empty\else\expandafter\writer\expandafter{\init}\fi
}
\makeatother
\begin{document}
\writer{Takes several words as argument}
\end{document}
这是您想要的 LaTeX3 实现。请注意,这相当简单,还可以避免混淆\split
和\writer
。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\bronislavsplit}{m}
{
\bronislav_split:n { #1 }
}
\seq_new:N \l_bronislav_input_seq
\tl_new:N \l_bronislav_last_tl
\cs_new_protected:Npn \bronislav_split:n #1
{
% split the input at spaces
\seq_set_split:Nnn \l_bronislav_input_seq { ~ } { #1 }
% start the recursion
\__bronislav_iterate:
}
\cs_new_protected:Npn \__bronislav_iterate:
{
\seq_if_empty:NF \l_bronislav_input_seq
{% this is executed only if the sequence is not empty
% for debugging, show the what's in the sequence
\typeout{writer: ~ \seq_use:Nn \l_bronislav_input_seq { ~ }}
% split off the last element
\seq_pop_right:NN \l_bronislav_input_seq \l_bronislav_last_tl
\seq_map_inline:Nn \l_bronislav_input_seq
{
% do something with each item
\bronislav_do_item:n { ##1 }
}
% do something with the last item
\bronislav_do_last_item:V \l_bronislav_last_tl
% keep on the recursion
\__bronislav_iterate:
}
}
\cs_new_protected:Npn \bronislav_do_item:n #1
{
\typeout { item: ~ #1 }
}
\cs_new_protected:Npn \bronislav_do_last_item:n #1
{
\typeout { last ~ item: ~ #1 }
}
% generate a variant because a variable is passed
\cs_generate_variant:Nn \bronislav_do_last_item:n { V }
\ExplSyntaxOff
\begin{document}
\bronislavsplit{Takes several words as argument}
\end{document}
这是终端上的输出:
writer: Takes several words as argument
item: Takes
item: several
item: words
item: as
last item: argument
writer: Takes several words as
item: Takes
item: several
item: words
last item: as
writer: Takes several words
item: Takes
item: several
last item: words
writer: Takes several
item: Takes
last item: several
writer: Takes
last item: Takes
当然,您希望使用最后两个命令做一些更有用的事情。