这个宏相当老了,而且我不太了解纯 TeX。MWE:
\documentclass{article}
\setlength{\unitlength}{0.06em}
\newlength{\cellsize} \setlength{\cellsize}{18\unitlength}
\newsavebox{\cell}
\sbox{\cell}{\begin{picture}(18,18)
\put(0,0){\line(1,0){18}}
\put(0,0){\line(0,1){18}}
\put(18,0){\line(0,1){18}}
\put(0,18){\line(1,0){18}}
\end{picture}}
\newcommand\cellify[1]{\def\thearg{#1}\def\nothing{}%
\ifx\thearg\nothing
\vrule width0pt height\cellsize depth0pt\else
\hbox to 0pt{\usebox{\cell} \hss}\fi%
\vbox to \cellsize{
\vss
\hbox to \cellsize{\hss$#1$\hss}
\vss}}
\newcommand\tableau[1]{\vtop{\let\\\cr
\baselineskip -16000pt \lineskiplimit 16000pt \lineskip 0pt
\ialign{&\cellify{##}\cr#1\crcr}}}
\begin{document}
\tableau{ & & & 5 & 6 & 8\\ & & 1 & 2 & 3\\ 3 & 5 & 7 & 5}
\end{document}
至于我的困惑,主要来自于
\ialign{&\cellify{##}\cr#1\crcr}}}
我知道嵌套宏定义中的双哈希会扩展为单个哈希并允许内部宏接受参数,但这似乎不是这里发生的事情。我不知道单个哈希单独意味着什么。我尝试搜索,但只能找到例如而不是或只是出现的解释。##1
它##
似乎#
以某种方式分解对齐字符处的 #1 输入并将它们传递给 cellify,但具体细节对我来说是黑魔法。
我之所以感到疑惑,是因为我试图扩展宏,以便在使用某个特定字符时使用不同的图片(虚线),但只是\if
在里面添加了一个额外的分支,\cellify
与#1
例如 -
不起作用。好吧,如果你\cellify
自己调用它,它确实起作用了,但\tableau
似乎给了它更多的令牌。
我对普通 TeX 的使用经验很少,而且这个宏中的很多其他内容也让我感到困惑,因为网上似乎没有很好的文档记录 ( \ialign
; \cr
; \crcr
; \hss
; \vss
)。如能提供任何解释,我将不胜感激。谢谢。
答案1
##
在宏定义中放入#
替换文本(这就是为什么##1
将所需内容放在#1
嵌套定义中)。
因此,如果的参数\cellify
是X
,那么当其展开时,#1
将被替换为X
,并被##
替换为#
所以
\ialign{&\cellify{##}\cr#1\crcr}
扩展为
\ialign{&\cellify{#}\cr X\crcr}
\ialign
\halign
是一个在纯 TeX 中定义的命令(并且在 latex 中也被继承),它使用比 latex 更接近原始的语法tabular
,但本质上做同样的事情。
第一行(直到第一行\cr
是对齐模板。这里它以 开始,&
这是一种特殊的语法,意味着允许任意数量的列,并且每个单元格都会获得代码,\cellify{...}
其中...
s 之间的单元格内容&
。
在此示例中,表格中只有一个单元格,其排版方式如下
\cellify{X}
这是一个检测具有 just 的单元格-
并制作具有 just 的单元格!
作为演示的版本。
\documentclass{article}
\setlength{\unitlength}{0.06em}
\newlength{\cellsize} \setlength{\cellsize}{18\unitlength}
\newsavebox{\cell}
\sbox{\cell}{\begin{picture}(18,18)
\put(0,0){\line(1,0){18}}
\put(0,0){\line(0,1){18}}
\put(18,0){\line(0,1){18}}
\put(0,18){\line(1,0){18}}
\end{picture}}
\def\dashtest{-}
\newcommand\cellify[1]{\def\thearg{#1}\def\nothing{}%
\ifx\thearg\nothing
\vrule width0pt height\cellsize depth0pt\else
\ifx\thearg\dashtest
\hbox to 0pt{\usebox{\cell}\kern-2pt!\hss}%or whetever
\def\thearg{}%
\else
\hbox to 0pt{\usebox{\cell} \hss}\fi\fi
\vbox to \cellsize{
\vss
\hbox to \cellsize{\hss$\thearg$\hss}
\vss}}
\newcommand\tableau[1]{\vtop{\let\\\cr
\baselineskip -16000pt \lineskiplimit 16000pt \lineskip 0pt
\ialign{&\cellify{##}\cr#1\crcr}}}
\begin{document}
\tableau{ & &-& 5 & 6 & 8\\ & & 1 & 2 & 3\\ 3 & 5 & 7 & 5}
\end{document}
答案2
让我们从主要部分开始:
\ialign{&\cellify{##}\cr#1\crcr}
与往常一样,#1
将被调用时给出的参数替换,因此它不太相关。该宏\ialign
在 Plain TeX 中定义为
\everycr{}\tabskip\z@skip\halign
所以它是一个“初始化”对象\halign
,其中的一些参数被清除。重要的对象是原始对象\halign
,它是所有表的基础。
该原语的语法是
\halign{<templates>\cr<body of the table>}
其中<body of the table>
由 单元格组成,单元格之间用 分隔&
,行与行之间用 结束。其他内容可以作为 的参数\cr
放在 之后。\cr
\noalign
以下<templates>
是每个表列的规范,同样用&
。模板由三部分组成,传统上称为你和五,并在它们之间#
插入一个“占位符”,它代表表格主体中的单元格内容。在我们的例子中,我们有一个模板;初始&
值表示列数未指定,所有列将共享相同的模板。这里
你=
\cellify{
五=}
这意味着 TeX 确定单元格的内容后,它将用 替换它\cellify{<cell contents>}
。
就是这么简单!模板与单参数宏非常相似,但使用的#1
不仅仅是。#
为什么要加倍?因为它在定义中,与参数中的双井号(数字符号,哈希字符)##1 是什么意思?
那么,当\tableau
展开时,正确的单身的 #
将出现在主输入流中。
原语的意思是:当且仅当参数不提供结尾时才\crcr
添加(根据指令,结尾可能是)。需要结尾才能完成其工作,此技巧允许用户在使用时不指定尾随。\cr
#1
\cr
\let\\\cr
\\
\cr
\halign
\\
\tableau
您\hss
可以阅读\vss
TeX 按主题分类(单击链接或texdoc texbytopic
在终端窗口中输入)或在 TeXbook 中;书中有更多关于的内容\halign
。
答案3
这里讨论的宏是纯 TeX 和 LaTeX 方法的奇妙混合。在纯 TeX 中,它看起来像这样:
\def\tableau#1{\vbox{\offinterlineskip \let\\=\cr \ialign{&\cellify{##}\cr #1\crcr}}}
\def\cellify#1{\ifx^#1^\else
\vbox{\kern-.2pt\hrule
\hbox to18pt{\kern-.2pt\vrule height12pt depth6pt\hss#1\unskip\hss\vrule\kern-.2pt}
\hrule \kern-.2pt}%
\fi
}
\tableau{ & & & 5 & 6 & 8\\ & & 1 & 2 & 3\\ 3 & 5 & 7 & 5}
\end
如果需要更改-
为X
(例如),则可以替换\hss#1\unskip
为\hss\tableauA#1\unskip
,然后可以定义:
\def\tableauA#1#2\unskip{\ifx-#1x\else#1#2\unskip\fi}
\tableau{ & & - & 5 & 6 & 8\\ & & 1 & 2 & 3\\ 3 & 5 & 7 & 5}