以下是我对表格类内容的操作方式。我想知道是否有比\bool_while_do
我使用的方法更好的方法?
以下 MWE 是我被要求做的起点这个帖子(从我的角度来看最困难的事情已经完成了)。
\documentclass{article}
% Sources
% * https://tex.stackexchange.com/a/475291/6880
% * https://tex.stackexchange.com/a/558343/6880
\usepackage{xparse}
\ExplSyntaxOn
\seq_new:N \l__tnscalc_splittab_seq
\seq_new:N \l__tnscalc_subseq_seq
\int_new:N \l__tnscalc_nbline_int
\int_new:N \l__tnscalc_numcol_int
\tl_new:N \l__tnscalc_xline_temp_tl
\tl_new:N \l__tnscalc_pline_temp_tl
% #1 : line separator
% #2 : cell separator
% #3 : content
\NewDocumentCommand{\splittab}{m m +m}
{
\tnscalc_splittab:nnn{#1}{#2}{#3}
}
% The internal version of the general purpose macro
\cs_new_protected:Nn \tnscalc_splittab:nnn
{
% #1 : line separator
% #2 : cell separator
% #3 : content
% A group allows nesting
\group_begin:
% Split into parts
\seq_set_split:Nnn \l__tnscalc_splittab_seq { #1 } { #3 }
\int_set:Nn \l__tnscalc_nbline_int { \seq_count:N \l__tnscalc_splittab_seq } % why?
% First column
\seq_pop_left:NN \l__tnscalc_splittab_seq \l__tnscalc_xline_temp_tl
\seq_set_split:NnV \l__tnscalc_x_seq { #2 } \l__tnscalc_xline_temp_tl
\seq_pop_left:NN \l__tnscalc_splittab_seq \l__tnscalc_pline_temp_tl
\seq_set_split:NnV \l__tnscalc_p_seq { #2 } \l__tnscalc_pline_temp_tl
\int_set:Nn \l__tnscalc_numcol_int { \seq_count:N \l__tnscalc_x_seq }
% Pop the column by column.
\bool_while_do:nn { \int_compare_p:nNn \l__tnscalc_numcol_int > 0 }{
\seq_pop_left:NN \l__tnscalc_x_seq \l__tnscalc_x_tl
\seq_pop_left:NN \l__tnscalc_p_seq \l__tnscalc_y_tl
(\int_use:N \l__tnscalc_numcol_int :: \l__tnscalc_x_tl ; \l__tnscalc_y_tl)
\int_add:Nn \l__tnscalc_numcol_int {-1}
}
\group_end:
}
\ExplSyntaxOff
\begin{document}
\splittab{\\}{&}{ a & b & c \\ 1 & 2 & 3}
\end{document}
答案1
如果你没有固定数量的项目需要处理,那么这会有点棘手,但当然是可行的。\tnscalc_splittab:nnn
下面的实现将把序列拆分两次,第一次在第一个分隔符处,第二次在第二个分隔符处,然后将其重组为:
\__tnscalc_item:nw {a}{b}{c}\q_recursion_tail
\__tnscalc_item:nw {1}{2}{3}\q_recursion_tail
\__tnscalc_item:nw {\q_nil }\q_stop \q_recursion_stop
然后 every\__tnscalc_item:nw
会收集它后面的项目(结束标记 除外)\q_nil
,并将收集的项目传递给\__tnscalc_do:n
。经过几个扩展步骤后,您将获得:
\__tnscalc_do:n {{a}{1}}
\__tnscalc_item:nw {b}{c}\q_recursion_tail
\__tnscalc_item:nw {2}{3}\q_recursion_tail
\__tnscalc_item:nw {\q_nil }\q_stop \q_recursion_stop
删除第一批项目。再删除一些项目后,您将获得:
\__tnscalc_do:n {{a}{1}} % already executed
\__tnscalc_do:n {{b}{2}}
\__tnscalc_item:nw {c}\q_recursion_tail
\__tnscalc_item:nw {3}\q_recursion_tail
\__tnscalc_item:nw {\q_nil }\q_stop \q_recursion_stop
然后将使用最后一批,\q_recursion_tail
标记将发出列表末尾的信号,您将获得:
\__tnscalc_do:n {{a}{1}} % already executed
\__tnscalc_do:n {{b}{2}} % already executed
\__tnscalc_do:n {{c}{3}} % already executed
由于项目数量是可变的,因此每列传递时都会\__tnscalc_do:n
用括号括住所有项目,因此\__tnscalc_do:n
您必须确定项目数量(\tl_count:n {#1}
可能有帮助)并相应地处理它们。您还可以使用 映射到列中的每个项目\tl_map_inline:nn {#1} { <code with ##1> }
。
映射确保\__tnscalc_do:n
始终接收相同数量的项目。任何不完整的列都会被忽略。
要获取代码的输出,您可以定义\__tnscalc_do:n
为:
\cs_new_protected:Npn \__tnscalc_do:n #1
{
(\int_use:N \l__tnscalc_numcol_int :: \use_i:nn #1 ; \use_ii:nn #1 )
\int_decr:N \l__tnscalc_numcol_int
}
\use_i:nn
(请注意,和的使用\use_ii:nn
假定只有两个\\
以 - 分隔的项目;如果数字不同,则不能再使用这两个!)
将的定义更改\__tnscalc_do:n
为:
\cs_new_protected:Npn \__tnscalc_do:n #1
{
Column~\int_use:N \l__tnscalc_numcol_int :~
\tl_map_inline:nn {#1}
{ (##1) }
\par
\int_decr:N \l__tnscalc_numcol_int
}
对于相同的输入,产生:
\__tnscalc_do:n
您可以对 做出参数的定义\splittab
,但我将其留作练习。
代码如下:
\documentclass{article}
\RequirePackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand \splittab { m m +m }
{ \tnscalc_splittab:nnn {#1} {#2} {#3} }
% Variables
\int_new:N \l__tnscalc_numcol_int
\seq_new:N \l__tnscalc_tmp_seq
\tl_new:N \l__tnscalc_items_tl
% Main function
\cs_new_protected:Nn \tnscalc_splittab:nnn
{
\group_begin:
\tl_clear:N \l__tnscalc_items_tl
\int_set_eq:NN \l__tnscalc_numcol_int \c_max_int
\seq_set_split:Nnn \l__tnscalc_tmp_seq {#1} {#3}
\seq_map_inline:Nn \l__tnscalc_tmp_seq
{
\seq_set_split:Nnn \l__tnscalc_tmp_seq {#2} {##1}
\__tnscalc_seq_set_map:NNn \l__tnscalc_tmp_seq
\l__tnscalc_tmp_seq { {####1} }
\int_set:Nn \l__tnscalc_numcol_int
{
\int_min:nn { \l__tnscalc_numcol_int }
{ \seq_count:N \l__tnscalc_tmp_seq }
}
\tl_put_right:Nx \l__tnscalc_items_tl
{
\exp_not:N \__tnscalc_item:nw
\seq_use:Nn \l__tnscalc_tmp_seq { }
\exp_not:N \q_recursion_tail
}
}
\tl_put_right:Nn \l__tnscalc_items_tl
{ \__tnscalc_item:nw { \q_nil } \q_stop }
\tl_use:N \l__tnscalc_items_tl \q_recursion_stop
\group_end:
}
\cs_new_protected:Npn \__tnscalc_item:nw
{ \__tnscalc_iterate_collect:nnw { } }
\cs_new_protected:Npn \__tnscalc_iterate_collect:nnw #1 #2
{
\quark_if_recursion_tail_stop:n {#2}
\quark_if_nil:nTF {#2}
{ \__tnscalc_iterate_collect_end:nw {#1} }
{ \__tnscalc_iterate_collect_more:nw { #1{#2} } }
}
\cs_new_protected:Npn \__tnscalc_iterate_collect_more:nw #1 #2
\__tnscalc_item:nw #3 \q_stop
{ \__tnscalc_iterate_collect:nnw {#1} #3 \__tnscalc_item:nw #2 \q_stop }
\cs_new_protected:Npn \__tnscalc_iterate_collect_end:nw #1
\__tnscalc_item:nw #2 \q_stop
{
\__tnscalc_do:n {#1}
\__tnscalc_item:nw #2 \__tnscalc_item:nw { \q_nil } \q_stop
}
% Compatibility for older expl3
\cs_if_exist:NTF \seq_set_map_x:NNn
{ \cs_new_eq:NN \__tnscalc_seq_set_map:NNn \seq_set_map:NNn } % newer expl3
{
\cs_new_protected:Npn \__tnscalc_seq_set_map:NNn #1 #2 #3
{ \seq_set_map:NNn #1 #2 { \exp_not:n {#3} } } % older expl3
}
%
% In this macro, #1 will have as many items as
% there are \\-separated items in your list.
%
% You can iterate over those items with \tl_map_inline:nn
% or you can have some other macro process them.
\cs_new_protected:Npn \__tnscalc_do:n #1
{
(\int_use:N \l__tnscalc_numcol_int :: \use_i:nn #1 ; \use_ii:nn #1 )
\int_decr:N \l__tnscalc_numcol_int
% Column~\int_use:N \l__tnscalc_numcol_int :~
% \tl_map_inline:nn {#1}
% { (##1) }
% \par
% \int_decr:N \l__tnscalc_numcol_int
}
\ExplSyntaxOff
\begin{document}
\splittab{\\}{&}{ a & b & c \\ 1 & 2 & 3 }
\end{document}