
как мне динамически сформировать таблицу, заполняя ее динамическими данными?
Пример: Допустим, у меня есть
\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
Ответ Хайкообъяснил проблему с вашим кодом, как ее решить, а также как действовать в другом стиле, используя расширяемый целочисленный цикл (спасибо \numexpr
).
Ответ А.Эллеттапредоставляет еще два решения с использованием pgffor
и pgfkeys
.
ответ DJPэто метод, использующий внешний инструмент, в данном случае Python code
управляемый мощным Sage
via sagetex
.
Все эти ответы в той или иной форме требуют некоторой дополнительной работы, например (для тех TeX
) скрытие табуляции &
в макросе, или выполнение глобальных определений с помощью \xdef
, или подготовка регистра списка токенов, или использование различных видов TeX
условных операторов.
ThexintFor
из пакетаxinttoolsявляется альтернативой (см.Как выполнить итерацию по списку, разделенному запятыми?), что избавляет от необходимости писать дополнительное кодирование:
\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}
В любом случае результирующие таблицы будут следующими: