![循环和字符 &](https://linux22.com/image/307186/%E5%BE%AA%E7%8E%AF%E5%92%8C%E5%AD%97%E7%AC%A6%20%26.png)
我找不到此代码中的问题
\documentclass{article}
\newcount\mt
\newcount\mtt
\newcommand{\mttab}[2]{%
\mt=0
\loop
\advance\mt by 1
\mtt=1
{\loop
\advance\mtt by 1
&
\ifnum\mtt<#2
\repeat}
\\
\ifnum\mt<#1
\repeat
}
\begin{document}
\begin{tabular}{|c|c|c|}
\hline
col1 & col2 & col3 \\
\hline
\mttab{15}{3}
\end{tabular}
\end{document}
编辑
这个问题的目的是以最简单的方式创建一个空表格
答案1
回答之前如何,没有唯一的答案(你的解决方案是一个很好的答案),我会尝试记录为什么你的初次尝试失败了。这些知识可能有助于分析更复杂的情况。
首先,我记得关键点是你正试图从表格内部扩展一些宏。
表格单元格会创建组,因此所有分配都应设为全局。因此,我建议在此使用 LaTeX 计数器,因为在 LaTeX 中,它们始终是全局管理的。
无法在表格单元格内部使用预定义的 TeX 分隔宏(具有诸如 的定义
\def\foo #1\repeat{stuff with #1}
),如果#1
将获得可见的&
:\foo A & B\repeat C
,除了if\foo
是单元格中的第一个内容(意味着之前没有出现任何不可扩展的内容,如 a\relax
或 a\def
或 a\setcounter
)。
修复:
如果你需要做出影响其他单元格的定义或分配,则必须将它们设为全局的,
如果您需要将其
&
作为分隔宏的一部分来获取,则必须以某种方式隐藏它,通常在括号组内,或者以\TAB
先前\def\TAB{&}
或类似\firstofone{&}
(\def\firstofone #1{#1}
) 的形式书写。
如果我们按照这些要求修复您原来的代码,是否可以解决问题?
还没有,因为 LaTeX 循环构造当地的 \def
:
$ latexdef loop
\loop:
\long macro:#1\repeat ->\def \iterate {#1\relax \expandafter \iterate \fi }\iterate \let \iterate \relax
你的自己的答案给出了正确的解决方法:在单个单元格中使用所有内容,完全通过 来准备宏和要插入的标记\g@addto@macro
。这有效地解决了您的问题,并且您可以使用\count
而不是计数器。上面的第 2 点呢?好吧,&
被适当地隐藏在 中\emttok{&}
(这对于外部循环来说不是必需的,但如果没有它,内部循环\loop
就会看到裸露的&
)。
让我简要讨论一下另一种方法,它不通过构造 来进行\mttok
,而是尝试逐行逐单元格地工作。最优雅的是一些可扩展的循环,但让我们首先坚持使用 LaTeX \loop
,看看可以做些什么。
首先我们需要将其设为 的全局定义\iterate
。但是我们马上就遇到了嵌套的问题。使用这种方法,唯一的方法是定义两个不同的循环宏,例如\gloop
和\ggloop
。在这样做之前,还有更多问题需要提及。
在 的定义中,
\relax
后面的 存在问题。例如,这将执行。但这会带来灾难性的后果,因为会正确终止测试,但它会保留在标记流中,并且在 之后, 会强制开始新的一行。因此,在表格底部会出现一个空的部分行的问题。我们必须摆脱。我们可以用空格替换它以确保终止测试,或者我们确保始终使用自终止数字,将约束移到调用方。完成所有这些后,我们最终得到了这种方法:#1
\iterate
\ifnum 1=1\relax\expandafter\iterate\fi
\relax
\\
\relax
\ifnum
\ifnum
\documentclass{article}
\newcommand*\TAB{&}
% alternative to LaTeX's loop, for use in contexts were definitions
% must be made global to survive
% Thus we do global things, and we use distinct terminators to allow nesting.
% Careful that tests at the end of #1 must be self-terminating
\long\def\gloop #1\grepeat
{\gdef \giterate {#1\expandafter \giterate \grepeat }\giterate }
\long\def\ggloop #1\ggrepeat
{\gdef \ggiterate {#1\expandafter \ggiterate \ggrepeat }\ggiterate }
\let\grepeat\fi
\let\ggrepeat\fi
\newcounter{mt}
\newcounter{mtt}
\newcommand{\mttab}[2]{%
\setcounter{mt}{0}%
\gloop
\stepcounter{mt}% careful with spaces from line endings
\setcounter{mtt}{1}%
(\themt) % to see what happens
\ggloop
\themtt % again to see where we are
\stepcounter{mtt}%
\TAB
\ifnum #1>\value{mtt}%
\ggrepeat
\themtt
\\
\ifnum #2>\value{mt}%
\grepeat
}
\begin{document}
\begin{tabular}{|c|c|c|}
\hline
col1 & col2 & col3\\
\hline
\mttab{3}{15}
\hline
\end{tabular}
\end{document}
这会产生(我知道您要求空单元格,但我添加了内容以使事物可视化):
更优雅的方式是使用可扩展循环。LaTeX 有一个这样的循环:\@whilesw
。这个答案解释了如何使用它。这里给出以下方法:
\documentclass{article}
\newcounter{mt}
\newcounter{mtt}
% recycling the \@whilesw of LaTeX into a num test
% \ifnum tests must be properly self-terminating
% I define the \xwhilenum slightly differently from my earlier use of it
% https://tex.stackexchange.com/a/142562/4686
\makeatletter
\newcommand*{\xwhilenum}[1]{\@whilesw{\ifnum #1}\fi }
\makeatother
% for reference, the LaTeX kernel definitions:
%\@whilesw:
%\long macro:#1\fi #2->#1#2\@iwhilesw {#1#2}\fi \fi
%\@iwhilesw:
%\long macro:#1\fi ->#1\expandafter \@iwhilesw \else \@gobbletwo \fi {#1}\fi
\newcommand{\mttab}[2]{%
\setcounter{mt}{#2}%
\xwhilenum{\value{mt}>0 }% space after 0 is important
{% must use braces here
\addtocounter{mt}{-1}%
\setcounter{mtt}{#1}%
(\themt) % to see where we are
\xwhilenum{\value{mtt}>1 }% careful, N cells = N-1 &
{%
\themtt % again to see where we are
\addtocounter{mtt}{-1}%
&
}% end of inner \xwhilenum
\themtt
\\
}% end of external \xwhilenum
}
\begin{document}
\begin{tabular}{|c|c|c|}
\hline
col1 & col2 & col3\\
\hline
\mttab{3}{15}
\hline
\end{tabular}
\end{document}
它给出(为了改变,我向下数):
更优雅的方式也许是完全不使用计数器:借助 可以进行纯可扩展循环\numexpr
。但我的答案已经有点长了,因此我就到此为止。
答案2
根据标题生成多行空单元格非常容易expl3
:只需嵌套两个\prg_replicate:nn
循环。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\emptytab}{mm}
{% #1 is the comma separated list of headers, #2 the number of empty rows
\touhami_emptytab:nn { #1 } { #2 }
}
\int_new:N \l_touhami_emptytab_cols_int
\clist_new:N \l_touhami_emptytab_headers_clist
\cs_new_protected:Npn \touhami_emptytab:nn #1 #2
{
% store the headers in a clist
\clist_set:Nn \l_touhami_emptytab_headers_clist { #1 }
% count the number of headers
\int_set:Nn \l_touhami_emptytab_cols_int { \clist_count:n { #1 } }
% make a tabular
\begin{tabular}{|*{\l_touhami_emptytab_cols_int}{c|}}
\hline
\clist_use:Nn \l_touhami_emptytab_headers_clist { & } \\ \hline
\prg_replicate:nn { #2 } % repeat #2 times
{
% produce <number of cols - 1> cell separators
\prg_replicate:nn { \l_touhami_emptytab_cols_int - 1 } { & }
\\ \hline
}
\end{tabular}
}
\ExplSyntaxOff
\begin{document}
\emptytab{col1,col2,col3}{15}\qquad
\emptytab{col1,col2}{3}
\end{document}
答案3
这是一个解决方案
\documentclass{article}
\newcommand{\mttok}{}
\newcount\mt
\newcount\mtt
\makeatletter
\newcommand{\emttok}[1]{\g@addto@macro{\mttok}{#1}}
\makeatother
\newcommand{\mttab}[2]{%
\renewcommand{\mttok}{}
\mt=0
\loop
\advance\mt by 1
\mtt=1
{\loop
\advance\mtt by 1
\emttok{&}%
\ifnum\mtt<#2
\repeat}%
\emttok{\\\hline}%
\ifnum\mt<#1
\repeat\mttok
}
\begin{document}
\begin{tabular}{|c|c|c|}
\hline
col1 & col2 & col3 \\
\hline
\mttab{15}{3}
\end{tabular}
\end{document}
答案4
这是一个略有不同的方法。我坚持你的\loop
方法,尽管我确实依赖etex
宏\unexpanded
。
\documentclass{article}
\makeatletter
\newcommand\mttab[2]{%%
\ae@build@\ae@table@row{#2}{}{&}{\noexpand\\\noexpand\hline}%%
\ae@build@\ae@table{#1}{\unexpanded\expandafter{\ae@table@row}}{}{}%%
\ae@table
}
%% |------------------------------------------|
%% | "item" refers to either "column" or "row"|
%% |------------------------------------------|
%% | #1=commmand sequence to save row/table |
%% | #2=number of interations to pass through |
%% | #3=content for each item |
%% | #4=inter-item content |
%% | #5=wrap up content |
%% |------------------------------------------|
\newcount\ae@build@cnt
\def\ae@build@#1#2#3#4#5{%%
\let#1\relax
\ae@build@cnt=0
\loop
\advance\ae@build@cnt by 1
\ifx#1\relax
\xdef#1{#3}%%
\else
\xdef#1{\unexpanded\expandafter{#1}#4#3}%%
\fi
\ifnum\ae@build@cnt<#2
\repeat
\xdef#1{\unexpanded\expandafter{#1}#5}%%
}
\makeatother
\begin{document}
\begin{tabular}{|c|c|c|}
\hline
col1 & col2 & col3 \\
\hline
\mttab{15}{3}
\end{tabular}
\end{document}
什么是好的关于这种方法,同一个宏既处理行的创建,也处理表其余部分的构建:不需要嵌套循环,除非隐含地。
这是一个相当不同的版本,在这里您传递环境的前言tabular
、第一行和行数。您不需要指定列数。
\documentclass{article}
\makeatletter
%% #1=preamble for tabular: should be simple "l", "r", or "c" separated only by "|"
%% #2=formatting for the first row of the table
%% #3=number of empty rows to generate
\newcommand\mttab[3]{%%
\ae@column@cnt=0 %%
\ae@count@columns#1\relax%%
\ae@build@\ae@table@row{\the\ae@column@cnt}{}{&}{\noexpand\\\noexpand\hline}%%
\ae@build@\ae@table{#3}{\unexpanded\expandafter{\ae@table@row}}{}{}%%
\ae@prepend@begin@table@\ae@table{#1}{#2}%%
\ae@append@end@table\ae@table
\ae@table
}
%% |-------------------------------------------|
%% | "item" refers to either "column" or "row" |
%% |-------------------------------------------|
%% | #1=commmand sequence to save row/table |
%% | #2=number of interations to pass through |
%% | #3=content for each item |
%% | #4=inter-item content |
%% | #5=wrap up content |
%% |-------------------------------------------|
\newcount\ae@build@cnt
\def\ae@build@#1#2#3#4#5{%%
\let#1\relax
\ae@build@cnt=0
\loop
\advance\ae@build@cnt by 1 %%
\ifx#1\relax
\xdef#1{#3}%%
\else
\xdef#1{\unexpanded\expandafter{#1}#4#3}%%
\fi
\ifnum\ae@build@cnt<#2
\repeat
\xdef#1{\unexpanded\expandafter{#1}#5}%%
}
\def\ae@prepend@begin@table@#1#2#3{%%
\xdef#1{\noexpand\begin{tabular}{#2}%%
\unexpanded{#3}%%
\unexpanded\expandafter{#1}}}
\def\ae@append@end@table#1{%%
\xdef#1{\unexpanded\expandafter{#1}\noexpand\end{tabular}}%%
}
%% This macro moves through the preable for the tabular
%% and counts the number of tokens which are not "|".
\newcount\ae@column@cnt
\def\ae@count@columns#1{%%
\def\ae@continue{}%%
\ifx#1\relax
\else
\let\ae@continue\ae@count@columns
\ifx#1|
\else
\advance\ae@column@cnt by 1 %%
\fi
\fi
\ae@continue}
\makeatother
\begin{document}
\mttab{|c|c|c|}{\hline col1 & col2 & col3 \\\hline}{15}
\end{document}
成功的关键是计算列数的宏。它假设除了列说明符之外l
,r
、 或c
仅|
出现在tabular
环境的前言中。