
como faço para gerar um tabular dinamicamente enquanto o preencho com dados dinâmicos?
Exemplo: digamos que eu tenha
\def\N{10}
e quero gerar uma tabela com \N
colunas, onde a célula superior de cada coluna contenha o número da coluna, como
\begin{tabular}{|c|c|c|...|c|}
1 & 2 & 3 & ... & N
\end{tabular}
Este foi o mais próximo que cheguei:
\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}
mas devido ao \expandafter
, isso me dá dez colunas, cada uma contendo 10.
Existe uma maneira de realizar algo como uma expansão parcial? Eu gostaria de expandir apenas o, \arabic{i}
mas é claro que não o &
.
Responder1
O problema é a linha:
\cols=\expandafter{\the\cols \arabic{i}}
\cols
é um registro de token, portanto \arabic{i}
não é expandido. Usar um adicional \expandafter
e imprimir o valor do contador em vez de \arabic
ajuda:
\cols=\expandafter{\the\expandafter\cols\the\value{i}}
Exemplo completo:
\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}
Versão expansível
O exemplo a seguir usa e-TeX \numexpr
. É tomado cuidado para conseguir um \if
ninho constante e &
não deve ser visto muito cedo.
\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}
Responder2
A resposta de Heikoexplicou o problema com seu código, como resolvê-lo e também como proceder em um estilo diferente usando um loop inteiro expansível (graças a \numexpr
).
Resposta de A.Ellettfornece mais duas soluções usando pgffor
e pgfkeys
.
Resposta do DJPé um método que utiliza uma ferramenta externa, aqui Python code
controlada pelo poderoso Sage
via sagetex
.
Todas essas respostas precisam, de uma forma ou de outra, de algum trabalho extra, como (para aquelas TeX
) ocultar a tabulação &
em uma macro, ou fazer definições globais com \xdef
, ou preparar um registro de lista de tokens, ou usar vários tipos de TeX
condicionais.
A xintFor
construção do pacotexinttoolsé uma alternativa (verComo iterar em uma lista separada por vírgula?) que poupa o trabalho dessa codificação extra:
\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}
O \xintifForFirst{YES}{N0}
teste consiste em inserir uma tabulação &
apenas na segunda e nas próximas células de uma determinada linha (aqui apenas uma linha). A \xintSeq
macro gera uma sequência aritmética de inteiros (por exemplo, {1}{2}{3}{4}
). O \xintFor* #1 in
itera sobre seu argumento (seu primo sem estrela \xintFor
itera sobre uma lista separada por vírgula), deixando #1
cada item um após o outro.
O mesmo código acima, mas usando um LaTeX
contador:
\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}
Aqui está agora um exemplo mais elaborado (pois usa dois loops aninhados) que constrói tabelas de multiplicação: \numexpr
é usado para a multiplicação do índice da linha pelo índice da coluna.
Aqui está o código:
\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}
Como usamos \xintFor*
dentro da definição de um LaTeX
comando do usuário, precisávamos dobrar o #
para evitar confusão entre o ##1
do loop e o #1
sendo o primeiro parâmetro do comando.
Responder3
Se você não se importa em aprender um pouco de Python, o sagetex
pacote pode lidar facilmente com tabelas dinâmicas; afinal Python é uma linguagem poderosa. A documentação do sagetex éaquie, em example.pdf, eles constroem o Triângulo de Pascal. Aqui está o código para algo mais próximo do que você pediu; a primeira linha é de 1 a 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}
Com a quantidade limitada de Python que você precisa aprender, imagino que provavelmente seja mais fácil do que programar em TeX. Você precisa saber: os loops for não incluem o último valor listado no intervalo e r é para string bruta e evita problemas que podem surgir por ter caracteres como barra invertida em sua string. Finalmente, %d serve para inserir números inteiros, %f para float e %s para string. O sagesilent
ambiente está compondo o código real que é inserido por meio de um arquivo sagestr
.
Responder4
Isso pode ser feito de várias maneiras, conforme já sugerido pelos comentários e outras respostas. Aqui estão duas soluções usando pgffor
e pgfkeys
.
Esta primeira solução funciona sem usar chaves:
\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}
Este segundo exemplo realiza a mesma tarefa usando chaves:
\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}
Em ambos os casos, as tabelas resultantes são: