我一直在尝试使用一个宏,将对象的结构抽象\halign
为多个列,正如我之前的问题中提到的那样“我可以在 \halign 的序言中使用宏吗?”对于问题的这一部分,\span
效果很好,但我还无法得到一个综合的解决方案。
我尝试使用这个代码:
\count1=4 \countdef\columns=1
\def\subpre{
& ##
\advance\columns by -1 \ifnum\columns>0 \span\subpre \fi
}
\def\preamb{
##
\advance \columns by -1
\the\columns
\ifnum\columns>0 \span\subpre \fi
\cr
}
\halign{\span\preamb
first line \cr
second line \cr
third line \cr
}
\bye
创建一个\halign
,可以通过改变分配给的数字来修改count1
,但这会导致错误:
! TeX capacity exceeded, sorry [input stack size=5000].
\subpre ...by -1 \ifnum \columns >0 \span \subpre
\fi
\subpre ...by -1 \ifnum \columns >0 \span \subpre
\fi
\subpre ...by -1 \ifnum \columns >0 \span \subpre
\fi
\subpre ...by -1 \ifnum \columns >0 \span \subpre
\fi
\subpre ...by -1 \ifnum \columns >0 \span \subpre
\fi
\subpre ...by -1 \ifnum \columns >0 \span \subpre
\fi
...
l.13 \halign{\span\preamb
我认为这是因为即使不应该由 if 语句评估, \span\subpre
in\subpre
也会自动扩展,但我不知道如何像在“标准”递归循环中那样有条件地扩展它。第 4 行 if 语句中有条件扩展代码的最佳方法是什么?我使用的是 Plain TeX。
答案1
正如 Henri Menke 在评论中所写,你的前言必须是可扩展的(并且每个应该扩展的标记前面都必须有\span
)。那么我们如何仅使用纯 TeX 构建一个完全可扩展的循环?我们使用\romannumeral
。如果\count1
是某个整数,那么我们可以使用\the\count1 000
来获得1000*\count1
。这用罗马数字表示为\count1
字母的精确倍数m
。\romannumeral
是可扩展的,因此我们可以在前言中使用它。然后我们的宏被重复,m
每次迭代消耗一个,直到m
用完:
\count1=4
\def\subpre#1{
\span\if m#1
& ##
\span\expandafter
\span\expandafter\subpre
\fi
}
\def\gobble#1{}
\def\preamb{
##
\span\expandafter
\span\expandafter\expandafter
\expandafter\span\expandafter\expandafter\expandafter\subpre
\expandafter\gobble\romannumeral\the\count1 000x
\cr
}
\halign{\span\preamb
first& line&val&x \cr
}
\bye
要理解\expandafter
中的所有 s \preamb
,请尝试像 TeX 一样思考:在 中\preamb
,我们首先将其添加##
到前导码中。然后是\span
,因此下一个标记被扩展。它是\expandafter
,因此我们跳过一个标记并扩展下一个标记,它(再次)是\expandafter
。这重复了 6 次,扩展了 中的第一个、第二个、第四个、第五个、第七个和第八个,\expandafter
直到\preamb
最终扩展\romannumeral\the\count1 000x
。这会扩展为\romannumeral4000x
,因此mmmmx
。没有\expandafter
s 和\romannumeral
扩展,我们剩下的是标记
\span\expandafter
\span\expandafter\subpre\gobble mmmmx
同样,我们有\span
,所以我们展开它。其余的\expandafter
s 最终会展开\gobble
,吞噬第一个m
。这相当于\advance \columns by -1
原始代码中的 。所以剩下的是
\span\subpre mmmx
最终开始扩张\subpre
。
另一方面,不将自己限制在可扩展构造上通常要容易得多。相反,您可以先构造前导码(例如,标记列表),然后只将完成的前导码插入\halign
:(基于 David Carlisle 对您原始问题的回答)
\newcount\columns
\newtoks\preambtoks
\def\myhalign{%
\afterassignment\xmyhalign
\columns=%
}
\def\xmyhalign{\afterassignment\xxmyhalign\let\tmp=}
\def\subpre{
\preambtoks\expandafter{\the\preambtoks & ##}
\advance\columns by -1 \ifnum\columns>0 \subpre \fi
}
\def\xxmyhalign{%
\preambtoks{##}%
\advance \columns by -1
\ifnum\columns>0 \subpre \fi
\halign\bgroup\span\the\preambtoks\cr
}
\myhalign3{
first line & second column \cr
1& 2&3 \cr
}
\bye
答案2
\span
前导上下文中的原语\halign
执行单个扩展步骤。
如果您想分配可变数量的列,则需要一些更复杂的代码。
这里我定义\preamb
将一个列模板(在示例中为\hfil#\hfil
)作为参数;该模板重复的次数与的当前值\columns
(假定为正值)一样多。
诀窍是使用\span
扩展\romannumeral-`@
,它进行递归扩展,直到找到不可扩展的标记。
该\subpre
宏被调用\columns-1
多次,并且每次第三个参数都包含迄今为止由另一列增强的前言。
% syntactic sugar
\long\def\firstoftwo#1#2{#1}
\long\def\secondoftwo#1#2{#2}
% the code
\newcount\columns
\def\preamb#1{#1\span\romannumeral-`@\subpre\columns{#1}{}}
\def\subpre#1#2#3{%
\ifnum\numexpr#1-1=0
\expandafter\firstoftwo
\else
\expandafter\secondoftwo
\fi
{#3\cr}%
{\expandafter\subpre\expandafter{\the\numexpr#1-1}{#2}{#3}}%
}
% the test
\tabskip=10pt
\columns=4
\halign{\span\preamb{\hfil#\hfil}%
1&2&3&4\cr
AAA&BB&CCCC&DDDD\cr
u&v&w&x\cr
}
\bye