
¿Cómo genero una tabla dinámicamente mientras la lleno con datos dinámicos?
Ejemplo: digamos que tengo
\def\N{10}
y quiero generar una tabla con \N
columnas, donde la celda superior de cada columna contiene el número de la columna, como
\begin{tabular}{|c|c|c|...|c|}
1 & 2 & 3 & ... & N
\end{tabular}
Esto fue lo más cerca que estuve:
\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}
pero debido a \expandafter
, esto me da diez columnas, cada una con un 10.
¿Hay alguna manera de realizar algo así como una expansión parcial? Me gustaría ampliar sólo el \arabic{i}
pero por supuesto no el &
.
Respuesta1
El problema es la línea:
\cols=\expandafter{\the\cols \arabic{i}}
\cols
es un registro de token, por lo que \arabic{i}
no se expande. Usar un valor adicional \expandafter
e imprimir el valor del contador en lugar de \arabic
ayuda:
\cols=\expandafter{\the\expandafter\cols\the\value{i}}
Ejemplo 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}
Versión ampliable
El siguiente ejemplo utiliza \numexpr
. Se tiene cuidado de conseguir una \if
anidación constante y &
no se debe ver demasiado pronto.
\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}
Respuesta2
La respuesta de Heiko.Explicó el problema con su código, cómo resolverlo y también cómo proceder en un estilo diferente usando un bucle de enteros expandible (gracias a \numexpr
).
La respuesta de A. Ellettproporciona dos soluciones más usando pgffor
y pgfkeys
.
La respuesta de DJPes un método que utiliza una herramienta externa, aquí Python code
manejada por el poderoso Sage
vía sagetex
.
Todas estas respuestas necesitan, de una forma u otra, algo de trabajo adicional, como (para aquellas TeX
) ocultar la tabulación &
en una macro, hacer definiciones globales con \xdef
, preparar un registro de lista de tokens o usar varios tipos de TeX
condicionales.
La xintFor
construcción del paquete.herramientas xintes una alternativa (ver¿Cómo iterar sobre una lista separada por comas?) que evita la molestia de esta codificación adicional:
\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}
La \xintifForFirst{YES}{N0}
prueba consiste en insertar una tabulación &
solo en la segunda y siguiente celda de la fila dada (aquí solo una fila). La \xintSeq
macro genera una secuencia aritmética de números enteros (por ejemplo {1}{2}{3}{4}
). Itera \xintFor* #1 in
sobre su argumento (su primo sin asterisco \xintFor
itera sobre una lista separada por comas), dejando #1
cada elemento uno tras otro.
El mismo código que el anterior pero usando más bien un 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}
Aquí hay ahora un ejemplo más elaborado (ya que utiliza dos bucles anidados) que construye tablas de multiplicar: \numexpr
se utiliza para multiplicar el índice de fila por el índice de columna.
Aquí está el 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 de la definición de un LaTeX
comando de usuario, necesitábamos duplicar #
para evitar confusión entre el ##1
bucle y #1
ser el primer parámetro del comando.
Respuesta3
Si no le importa aprender un poco de Python, el sagetex
paquete puede manejar tablas dinámicas fácilmente; después de todo, Python es un lenguaje poderoso. La documentación para sagetex esaquíy, en ejemplo.pdf, construyen el Triángulo de Pascal. Aquí está el código para algo más parecido a lo que solicitó; la primera fila es del 1 al 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}
Con la cantidad limitada de Python que necesitas aprender, me imagino que probablemente sea más fácil que programar en TeX. Necesita saber: los bucles for no incluyen el último valor enumerado en el rango y r es para cadenas sin formato y evita problemas que puedan surgir al tener caracteres como una barra invertida en su cadena. Finalmente, %d es para insertar números enteros, %f para flotante y %s para cadena. El sagesilent
entorno escribe el código real que luego se inserta mediante un archivo sagestr
.
Respuesta4
Esto se puede hacer de varias maneras, como ya lo sugieren los comentarios y otras respuestas. Aquí hay dos soluciones que usan pgffor
y pgfkeys
.
Esta primera solución funciona sin utilizar claves:
\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 ejemplo realiza la misma tarea usando claves:
\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}
En cualquier caso las tablas resultantes son: