在 \halign 的序言中使用循环

在 \halign 的序言中使用循环

我一直在尝试使用一个宏,将对象的结构抽象\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\subprein\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。没有\expandafters 和\romannumeral扩展,我们剩下的是标记

\span\expandafter
\span\expandafter\subpre\gobble mmmmx

同样,我们有\span,所以我们展开它。其余的\expandafters 最终会展开\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&#2}}%
}

% 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

在此处输入图片描述

相关内容