这是先前的问题。
我正在尝试破解单元格解析\matrix
,以便在代码的某个时刻我可以保存和操作整个单元格内容来决定如何呈现它。
我实际上非常接近这个结论:
\input tikz
\catcode`@ = 11
\tikzset{
my matrix/.style={
matrix,
execute at begin cell=\myBeginCell,
execute at empty cell=\myEmptyCell,
}
}
\def\myNil{\NOPE}
\def\myBeginCell{
\let\\\myNewLine
\pgfutil@ifnextchar\relax{}{\myDoTail}
}
\def\myNewLine{
\pgfutil@ifnextchar\myNil
{\expandafter\pgfmatrixendrow\pgfutil@gobble}
{\pgfmatrixendrow}
}
\bgroup
\catcode`&=\active
\gdef\myDoTail#1\\{
% \def\TAIL{#1}\show\TAIL%
\pgfutil@ifnextchar\myNil
{\myDoHead#1\\}
{\myDoHead#1&\myNil\\\myNil}
}
\gdef\myDoHead#1&{
% \def\HEAD{#1}\show\HEAD%
\node{#1};
\pgfutil@ifnextchar\myNil{\pgfutil@gobble}{&}
}
\egroup
\def\myEmptyCell{\node{$\emptyset$};}
% \def\myEmptyCell#1[#2#3{
% \node{$\emptyset$};%
% \pgfutil@ifnextchar\myNil%
% {#1[#2#3\pgfutil@gobble}
% {#1[#2#3}
% }
\catcode`@ = 12
\tikz\matrix[my matrix]{
1 & 2 & 3 \\
& 4 & 5 \\
6 & & 7 \\
8 & 9 & \\
};
\bye
发生了什么?
在每个单元格的开头,行的剩余部分都会用一些终止符进行标记。例如:A & B \\
变为A & B & \myNil \\ \myNil
。第一个终止符用于检测行何时结束,并允许\gdef\myDoHead#1&{...}
通过在末尾添加一个缓冲区单元格来匹配单元格,该缓冲区单元格会被检测到并被忽略。第二个终止符用于检测行是否已被标记,并在必要时重新定义以\\
将其删除。
我有什么问题?
空单元格的解析方式不同。这意味着当一行的最后一个单元格为空并且\myNil
添加了缓冲区单元格(包含),则不会检测到并擦除它。这意味着\myNil
会扩展并且一切都会崩溃。进行\myNil
无操作不是一个可接受的解决方案:仍然有一个额外的空单元格,实际上使行尾的所有空单元格加倍。
我尝试过什么?
我能想到的最好的就是上面评论的定义:
\def\myEmptyCell#1[#2#3{
\node{$\emptyset$};%
\pgfutil@ifnextchar\myNil%
{#1[#2#3\pgfutil@gobble}
{#1[#2#3}
}
需要这些奇怪的参数来跳过单元格关闭标记:
\pgfsys@endscope \let \pgf@matrix@signal@cell@end =\pgf@matrix@signal@cell@end \pgfutil@ifnextchar [{\pgf@matrix@cell@cont }{\pgf@matrix@cell@cont [0pt]}\myNil \\\myNil
为了参考(我认为)这是
\pgfsys@endscope\pgfmatrixnextcell\myNil\\\myNil
这个暂时的解决方案是不完整的(没有处理第二个问题\myNil
),但它似乎仍然没有做我想要的事情(引发我无法理解的常见参数不匹配错误)。
此外,我不确定这是否是个好主意,但除了直接摆弄之外我想不出任何其他办法pgfmodulematrix.code.tex
,如果可能的话我宁愿避免这样做。
我在要求什么?
要么给出可行的定义,\myEmpyCell
要么给出证明,证明它是不可能的,我应该放弃这个计划。这个问题被标记是plain-tex
因为我需要一个像 TikZ 本身一样通用的解决方案。
也非常欢迎提出思考和建议!
答案1
干得好:
\input tikz
\catcode`@ = 11
\tikzset{
my matrix/.style={
matrix,
execute at begin cell=\myBeginCell,
execute at empty cell=\myEmptyCell,
}
}
\def\myNil{\NOPE}
\def\myBeginCell{
\let\\\myNewLine
\pgfutil@ifnextchar\relax{}{\myDoTail}
}
\def\myNewLine{
\pgfutil@ifnextchar\myNil
{\expandafter\pgfmatrixendrow\pgfutil@gobble}
{\pgfmatrixendrow}
}
\bgroup
\catcode`&=\active
\gdef\myDoTail#1\\{
\pgfutil@ifnextchar\myNil
{\myDoHead#1\\}
{\myDoHead#1&\myNil\\\myNil}
}
\gdef\myDoHead#1&{
\node{#1};
\pgfutil@ifnextchar\myNil{\pgfutil@gobble}{&}
}
\egroup
% Here's the new chunk:
% Take up to the \pgfutil@ifnextchar
\def\myEmptyCell#1\pgfutil@ifnextchar[#2#3{
% evaluate the \pgfutil@ifnextchar so we can be assured of having a pair [#2].
% We can delete #1 which just has the no-op \let\pgf@matrix@signal@cell@end=\pgf@matrix@signal@cell@end .
\pgfutil@ifnextchar[{\myEmptyCell@#2}{\myEmptyCell@#3}
}
% Now we know there is "[brackets]" right before the beginning of the next cell, so we can find it.
\def\myEmptyCell@#1[#2]{
\pgfutil@ifnextchar\myNil{% If the next cell is the terminal cell,
% Don't reinsert #1[#2] because it would make this code run again on the fake last cell where it would throw an error.
\myEmptyCell@endline % and eat the \myNil's
}{
#1[#2] % Not the last cell, so just reinsert the start a new cell code.
}
}
\def\myEmptyCell@endline#1\myNil#2\myNil{#1#2}
\tikz
\matrix[my matrix,nodes=draw]{
1 &[10pt] &[6pt] \\[5pt]
3 & & 5\\
6 & 7 &\\
};
\bye
它是如何工作的?我们需要检查我们是否是最后一个单元格。诀窍是吃掉直到下一个单元格开头的标记,然后检查单元格是否以 开头\myNil
。如果是这样,我们找到两个\myNil
并将它们都删除。
唯一的问题是,当前单元格结尾的 & 在检查单元格是否为空的过程中已经扩展,因此为了到达下一个单元格的开头,我们无法像您预期的那样真空到下一个 & 符号。相反,看看代码&
:
> &=macro:
->\let \pgf@matrix@signal@cell@end =\pgf@matrix@signal@cell@end \pgfutil@ifnextchar [{\pgf@matrix@cell@cont }{\pgf@matrix@cell@cont [0pt]}.
\let\pgf@matrix@signal@cell@end\pgf@matrix@signal@cell@end
我们在这里看到的是pgf 使用的无操作,用于判断单元格是否为空(这就是&
已经展开的原因),后面跟着一个处理可选参数的检查&
。
因此,我们吸收了 和\pgfutil@ifnextchar
它的三个参数。接下来是下一个单元格的开头或 的可选参数&
。我们运行\pgfutil@ifnextchar
检查并确保插入了括号,然后我们可以将其放回去并扫描到[#2]
,我们知道下一个字符是新单元格的第一个字符。
现在,我们检查下一个字符是否是\myNil
。如果是\myNil
,则删除两个\myNil
,并删除我们吸收的启动新单元格代码,以便进入下一个单元格(我们不希望&
我们添加的终端成为第二个空单元格)。
否则,重新插入开始新的单元代码。