如何在用动态数据填充表格的同时动态生成表格?
例如:假设我有
\def\N{10}
我想生成一个带有\N
列的表格,其中每列的顶部单元格包含列号,例如
\begin{tabular}{|c|c|c|...|c|}
1 & 2 & 3 & ... & N
\end{tabular}
这是我得到最接近的答案:
\newtoks\cols
\cols={}
\newcounter{i}
\setcounter{i}{1}
\loop
\cols=\expandafter{\the\cols \arabic{i}}
\ifnum\value{i}<\N
\cols=\expandafter{\the\cols &}
\stepcounter{i}
\repeat
\begin{tabular}{|*{\N}{c|}}
\the\cols
\end{tabular}
但由于\expandafter
,这给了我十列,每列包含 10。
有没有办法执行类似部分扩展的操作?我只想扩展,\arabic{i}
但当然不是&
。
答案1
问题出在这一行:
\cols=\expandafter{\the\cols \arabic{i}}
\cols
是一个令牌寄存器,因此\arabic{i}
不会扩展。使用附加寄存器\expandafter
并打印计数器值而不是\arabic
有助于:
\cols=\expandafter{\the\expandafter\cols\the\value{i}}
完整示例:
\documentclass{article}
\newtoks\cols
\newcounter{i}
\newcount\N
\begin{document}
\N=10
\cols={}
\setcounter{i}{1}
\loop
\cols=\expandafter{\the\expandafter\cols\the\value{i}}
\ifnum\value{i}<\N
\cols=\expandafter{\the\cols &}
\stepcounter{i}
\repeat
\begin{tabular}{|*{\N}{c|}}
\the\cols
\end{tabular}
\end{document}
可扩展版本
以下示例使用 e-TeX 的\numexpr
。注意获取常量\if
嵌套,并且&
不能过早看到。
\documentclass{article}
\newcount\N
\makeatletter
\newcommand*{\Ncols}{%
\Ncols@aux{1}%
}
\newcommand*{\Ncols@aux}[1]{%
\ifnum#1>\N
\expandafter\@gobble
\else
\expandafter\@firstofone
\fi
{%
\ifnum#1<2 \expandafter\@gobble\fi\Ncols@amp
#1%
\expandafter\Ncols@aux\expandafter{\the\numexpr(#1+1)}%
}%
}
\newcommand*{\Ncols@amp}{&}
\makeatother
\begin{document}
\N=10
\begin{tabular}{|*{\N}{c|}}
\Ncols
\end{tabular}
\end{document}
答案2
Heiko 的回答解释了您的代码中的问题、如何解决它,以及如何使用可扩展整数循环以不同的方式进行(感谢\numexpr
)。
A.Ellett 的回答提供了另外两种使用pgffor
和的解决方案pgfkeys
。
DJP 的回答是一种使用外部工具的方法,这里通过Python code
强大的功能来处理。Sage
sagetex
这些答案都需要以某种形式进行一些额外的工作,例如(对于那些)在宏中TeX
隐藏制表符,或使用进行全局定义,或准备标记列表寄存器,或使用各种条件。&
\xdef
TeX
xintFor
包中的构造新工具是一种替代方案(参见如何迭代以逗号分隔的列表?),这样就省去了额外编码的麻烦:
\documentclass{article}
\usepackage{xinttools}
\newcommand{\N}{10}
\begin{document}
% \renewcommand{\N}{<nb of cols>}
\begin{tabular}{*{\N}c}
\xintFor* #1 in {\xintSeq {1}{\N}}\do {\xintifForFirst{}{&}#1}\\
\end{tabular}
\end{document}
测试是仅在给定行(此处只有一行)的第二个和下一个单元格中 \xintifForFirst{YES}{N0}
插入制表符。宏生成整数的算术序列(例如)。迭代其参数(其无星号表亲迭代逗号分隔的列表),让其每个项目一个接一个。&
\xintSeq
{1}{2}{3}{4}
\xintFor* #1 in
\xintFor
#1
与上面相同的代码,但是使用计数器LaTeX
:
\documentclass{article}
\usepackage{xinttools}
\newcounter{N}
\begin{document}
\setcounter{N}{10}
\begin{tabular}{*{\value{N}}c}
\xintFor* #1 in {\xintSeq {1}{\value{N}}}\do {\xintifForFirst{}{&}#1}\\
\end{tabular}
\end{document}
这里有一个更复杂的例子(因为它使用了两个嵌套循环),它构建了乘法表:\numexpr
用于将行索引与列索引相乘。
以下是代码:
\documentclass{article}
\usepackage{xinttools}
\newcommand\MultTable [4]{%
% #1, #2 row indices
% #3, #4 column indices: we need #4-#3+2 columns
\begin{tabular}{*{\numexpr#4-#3+2\relax}c}
\hline
% headerline
$\times$\xintFor* ##1 in {\xintSeq {#3}{#4}}\do{&\textbf{##1}}\\
\hline
% #2-#1+1 rows, ##1=dynamic index of each row, ##2 column index
\xintFor* ##1 in {\xintSeq {#1}{#2}}\do
{\textbf{##1}
\xintFor* ##2 in {\xintSeq {#3}{#4}}\do{&\the\numexpr ##1*##2\relax}
\\
}
\hline
\end{tabular}%
% efficiency note: one could do \edef\columnindices {\xintSeq {#3}{#4}}
% before the tabular
% and use \columnindices rather \xintSeq {#3}{#4} to avoid it being
% re-computed for each row
}
\begin{document}
\begin{table}[!htbp]
\centering
\MultTable {1}{10}{1}{10}
\caption{Multiplication table}
\end{table}
\begin{table}[!htbp]
\centering
\MultTable {123}{132}{91}{98}
\caption{Multiplication table}
\end{table}
\end{document}
正如我们\xintFor*
在用户命令的定义中使用的那样LaTeX
,我们需要将其加倍,#
以避免##1
循环的和#1
命令的第一个参数之间的混淆。
答案3
如果您不介意学习一点 Python,该sagetex
包可以轻松处理动态表;毕竟 Python 是一种强大的语言。sagetex 的文档是这里在 example.pdf 中,他们构造了帕斯卡三角形。以下是更接近您要求的代码;第一行是 1 到 N:
\documentclass{article}
\usepackage{sagetex}
\pagestyle{empty}
\begin{document}
\begin{sagesilent}
N = 5
M = 4
output = r"\begin{tabular}{"
for i in range(0,N):
output += r"|c"
output += r"|}"
for j in range(0,M):
for i in range(0,N-1):
output += r"%d & "%((j+1)*(i+1))
output += r"%d \\"%(N*(j+1))
output += r"\end{tabular}"
\end{sagesilent}
Here's the output:\\\\
\sagestr{output}
\end{document}
由于你需要学习的 Python 数量有限,我想它可能比用 TeX 编程更容易。你需要知道:for 循环不包括范围中列出的最后一个值,r 用于原始字符串,可避免字符串中出现反斜杠等字符可能引起的问题。最后,%d 用于插入整数,%f 用于浮点数,%s 用于字符串。环境sagesilent
正在排版实际代码,然后通过 插入sagestr
。
答案4
正如评论和其他答案所建议的那样,可以通过多种方式实现这一点。以下是使用pgffor
和 的两种解决方案pgfkeys
。
第一个解决方案无需使用密钥即可工作:
\documentclass{article}
\usepackage{pgffor}
\usepackage{etoolbox}
\makeatletter
\newcommand\aeDynamicTable{\ae@dynamicTable}
\def\ae@dynamicTable[#1]#2{%%
\let\ae@table@content\relax%%
\def\ae@new@column{&}
\def\ae@row{0}%%
\foreach \x in {1,...,#2}
{%%
\xdef\ae@row{\number\numexpr\ae@row+1\relax}%%
\ifx\relax\ae@table@content
\xdef\ae@table@content{\noexpand\@arabic{\x}}%
\else
\xdef\ae@table@content{\expandonce{\ae@table@content} \noexpand\@arabic{\x}}%%
\fi
\ifnum\ae@row=#1\relax
\xdef\ae@row{0}%%
\def\ae@new@line{\\}%%
\def\ae@new@column{}%%
\else
\def\ae@new@line{}%%
\def\ae@new@column{&}%%
\fi
\ifnum\x=#2\relax
\def\ae@new@line{}%%
\def\ae@new@column{}%%
\fi
\xdef\ae@table@content{\expandonce{\ae@table@content} \expandonce{\ae@new@column} \expandonce{\ae@new@line}}%
}%%
\begin{tabular}{|*{#1}{c|}}
\ae@table@content
\end{tabular}%%
}
\makeatother
\begin{document}
\aeDynamicTable[3]{5}
\aeDynamicTable[10]{35}
\end{document}
第二个示例使用键完成相同的任务:
\documentclass{article}
\usepackage{pgffor}
\usepackage{pgfkeys}
\usepackage{etoolbox}
\makeatletter
\def\ae@col@limit{}
\def\ae@max@cells{}
\pgfkeys{/ae/dynamic/table/.cd,
cols/.store in=\ae@col@limit,
cells/.store in=\ae@max@cells,
}
\newcommand\aeDynamicTable{\ae@dynamicTable}
\def\ae@dynamicTable#1{%%
\pgfkeys{/ae/dynamic/table/.cd,#1}%%
\let\ae@table@content\relax%%
\def\ae@new@column{&}
\def\ae@col{0}%%
\foreach \x in {1,...,\ae@max@cells}
{%%
\xdef\ae@col{\number\numexpr\ae@col+1\relax}%%
\ifx\relax\ae@table@content
\xdef\ae@table@content{\noexpand\@arabic{\x}}%
\else
\xdef\ae@table@content{\expandonce{\ae@table@content} \noexpand\@arabic{\x}}%%
\fi
\ifnum\ae@col=\ae@col@limit\relax
\xdef\ae@col{0}%%
\def\ae@new@line{\\}%%
\def\ae@new@column{}%%
\else
\def\ae@new@line{}%%
\def\ae@new@column{&}%%
\fi
\ifnum\x=\ae@max@cells\relax
\def\ae@new@line{}%%
\def\ae@new@column{}%%
\fi
\xdef\ae@table@content{\expandonce{\ae@table@content} \expandonce{\ae@new@column} \expandonce{\ae@new@line}}%
}%%
\begin{tabular}{|*{\ae@col@limit}{c|}}
\ae@table@content
\end{tabular}%%
}
\makeatother
\begin{document}
\aeDynamicTable{cols=3,cells=5}
\aeDynamicTable{cols=10,cells=35}
\end{document}
无论哪种情况,结果表都是: