我正在尝试自动创建某些表单,其中一部分工作是自动在类似表格的表单中创建正确数量的空白行。这导致我出现了以下奇怪的行为:
\documentclass{article}
\usepackage{expl3}
\usepackage{xparse}
\ExplSyntaxOn
\cs_new_eq:NN \clistMap \clist_map_inline:nn
\ExplSyntaxOff
\begin{document}
\begin{tabular}{|ll|}
\clistMap{one, two, three}{#1 &\\\hline}
\end{tabular}
\begin{tabular}{|ll|}
one &\\\hline
two &\\\hline
three &\\\hline
\end{tabular}
\end{document}
这两个表格的排版方式不同。我不明白为什么。第一个表格的三行后面有一个尾行。我说尾行,是因为下面的这个表格的排版方式与第一个表格相同。
\begin{tabular}{|ll|}
one &\\\hline
two &\\\hline
three &\\\hline
\\
\end{tabular}
作为最后一对尽可能简单的例子,请注意以下两个表的相同外观:
\begin{tabular}{|ll|}
\clistMap{}{#1 &\\\hline}
\end{tabular}
\medskip % So you can see where one table ends and the other begins.
\begin{tabular}{|ll|}
\\
\end{tabular}
我该如何去掉这个尾随的换行符?为了加分 --- 如果您愿意的话,我未来的回答者 --- 如何检测是否存在其他意外的控制序列?我尝试使用 fancyvrb 命令来执行此操作,但始终无法完全得到我想要的结果;我无法检测到控制序列\\
,其他字符也会
&
导致问题。我还尝试使用 trace 包,类似于\clistMap
扩展为大约 1000 行的扩展宏。
答案1
您需要使用\clist_map_function:nN
,定义一个临时函数:
\documentclass{article}
\usepackage{expl3}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\clistMap}{mm}
{
\cs_gset:Nn \__beelzebielsk_map:n { #2 }
\clist_map_function:nN { #1 } \__beelzebielsk_map:n
}
\ExplSyntaxOff
\begin{document}
\begin{tabular}{|l|l|}
\hline
\clistMap{one, two, three}{#1 &\\\hline}
\end{tabular}
\qquad
\begin{tabular}{|l|l|}
\hline
one &\\\hline
two &\\\hline
three &\\\hline
\end{tabular}
\end{document}
问题在于,\clist_map_inline:nn
当 TeX 意识到循环已结束时,它会让 TeX 处于新单元已启动的状态。
这实际上是这样\clist_map_inline:nn
做的:它定义了一个新的“未命名”函数并使用\clist_map_function:nN
它。但是,它还必须掩盖其踪迹,特别是为了便于嵌套使用,为此它使用了一个必须在最后逐步降低的计数器:这是创建不需要的额外行的操作。
更明确地说:\clist_map_inline:nn { <clist> } { <code> }
大致如此
\int_incr:Nn \<reserved>_int
\cs_gset:cn { <reserved> \int_eval:n \<reserved>_int :n } { <code> }
\clist_map_function:nc { <clist> } { <reserved> \int_eval:n \<reserved>_int :n }
\int_decr:Nn \<reserved>_int
传递给 so 的函数\clist_map_function:nc
取决于嵌套级别,因为保留计数器与 clist 映射相关联。这允许\clist_map_inline:nn
在其第二个参数中包含一个 inner \clist_map_inline:nn
,它将\clist_map_function:nc
使用不同的功能。
但是,\int_decr:Nn \<reserved>_int
对于你的情况,该操作将触发 TeX 开始一个新单元。TeX 表总是很叛逆;-)
。。
根据所提出的定义,计数器上的操作被跳过;当然你不能\clistMap
嵌套\clistMap
。
答案2
我相信每个人都会原谅我的有些离题的回答,这只是提供一个说明@egreg 解释的例子:
\documentclass{article}
\usepackage{xinttools}
\begin{document}
\begin{tabular}{|ll|}
\hline
\xintFor #1 in {one, two, three}:
{#1 &\\\hline}
\end{tabular}
\begin{tabular}{|ll|}
\hline
one &\\\hline
two &\\\hline
three &\\\hline
\end{tabular}
\end{document}
虽然\xintFor
行为不可扩展,但要小心不要做任何“纯扩展不消失”的事情在其末端。这样它就不会意外触发新行。但是,此约束对功能有一些限制作用\xintFor
(请参阅 xint.pdf 中方框内的“15.17 \xintifForFirst、\xintifForLast”注释)。
请注意,嵌套\xintFor
很好。
这里的补充说明是关于(放大时)在两个(相同)表格中的水平线和垂直线连接处看到的奇怪的“间隙”:添加\usepackage{array}
以修复该问题。
答案3
您可以使用以下方法使原始函数在此位置安全\noalign
\documentclass{article}
\usepackage{expl3}
\usepackage{xparse}
\ExplSyntaxOn
\cs_new_eq:NN \clistMap \clist_map_inline:nn
\ExplSyntaxOff
\begin{document}
\begin{tabular}{|ll|}
\noalign\bgroup\clistMap{one, two, three}{\egroup#1 &\\\hline\noalign\bgroup}\egroup
\end{tabular}
\begin{tabular}{|ll|}
one &\\\hline
two &\\\hline
three &\\\hline
\end{tabular}
\end{document}