The following is a loop over n columns. It has to be used as the first element of a row so you can only use it to loop over a whole row or the first n columns of a row. The first argument of \colloop is the number of columns it should loop over, the second is contents of each cell, with #1 being replaced by the column number.
\documentclass[border=3.14]{standalone} \newcounter{col} \makeatletter \newcommand\colloop@add[2] {% \expandafter\g@addto@macro\expandafter\colloop@\expandafter {\colloop@@{#1}}% \ifnum#2>\value{col} \g@addto@macro\colloop@{&}% \fi } \newcommand\colloop[2] {% \noalign {% \setcounter{col}{0}% \gdef\colloop@{}% \gdef\colloop@@##1{#2}% \loop\ifnum#1>\value{col} \stepcounter{col}% \expandafter\colloop@add\expandafter{\the\value{col}}{#1}% \repeat }% \colloop@ } \makeatletter \begin{document} \begin{tabular}[]{llll} \colloop{4}{column #1} \\ \colloop{4}{\multicolumn{1}{c}{c#1}} \\ \colloop{4}{\multicolumn{1}{r}{col#1}} \\ \end{tabular} \end{document}

EDIT: Alternative approach which is split in an expandable and an unexpandable part. With \Setupexpcolloop and \setupexpcolloop you can define what \expcolloop should expand to (again using #1 for the current column). The difference between the two defining macros is, that \setupexpcolloop can be used anywhere while \Setupexpcolloop can be used only at the start of a row. But if you use \setupexpcolloop you can't use \multicolumn or similar in the same cell (so use it outside of the tabular environment, or at the end of a cell which is already filled with contents). The advantage of this approach is, that you don't have to loop over an entire row or only the first n columns.
\documentclass[border=3.14]{standalone} \makeatletter \long\def\myfi@b\fi#1#2{\fi#2} \newcommand\setupexpcolloop[1] {% \gdef\expcolloop@##1{#1}% } \newcommand\Setupexpcolloop[1] {% \noalign{\setupexpcolloop{#1}}% } \newcommand\expcolloop[2] {% \expcolloop@{#1}% \ifnum#1<#2 \myfi@b \fi \@gobble {&\expandafter\expcolloop\expandafter{\the\numexpr#1+1\relax}{#2}}% } \makeatletter \begin{document} \begin{tabular}[]{|l|l|l|l|l|l|} \Setupexpcolloop{column #1} \expcolloop{1}{6} \\ \Setupexpcolloop{\multicolumn{1}{c|}{c#1}} col1 & \expcolloop{2}{6} \\ \Setupexpcolloop {% \ifnum#1=1 \multicolumn{1}{|c|}{hihi1}% \else \multicolumn{#1}{c|}{hihi#1}% \fi }% \expcolloop{1}{3} \\ \Setupexpcolloop{c#1} \expcolloop{1}{2}\setupexpcolloop{Cc#1} & \expcolloop{3}{6} \\ \end{tabular} \end{document}

Disclaimer: I do not endorse the usage of vertical rules in tables, this is just to show the effects of the \multicolumn usages.
Just because it is possible, here is another variant which is fully expandable and doesn't need to be split. This makes use of expkv-cs to parse a key=value list expandably, and of etl to expandably replace a token (here we use # to insert the column number; if you need # inside your looped columns for any reason you can use another single token with the token or t key).
\documentclass[border=3.14]{standalone} \usepackage{etl} \usepackage{expkv-cs} \ExplSyntaxOn \NewExpandableDocumentCommand \colloop { O{} m m } { \exp_args:Ne \exp_not:o { \exp_not:N \use_none:n \__emoro_colloop:nnn {#1} {#2} { & #3 } } } \ekvcSplitAndForward \__emoro_colloop:nnn \__emoro_colloop:nnNnn % define keys { long ~ start = 1 ,long ~ step = 1 ,long ~ token = # } \ekvcSecondaryKeys \__emoro_colloop:nnn % define shortcut names for keys { alias ~ s = step ,alias ~ t = token } \NewDocumentCommand \colloopsetup { m } { \ekvcChange \__emoro_colloop:nnn {#1} } \makeatletter \cs_if_exist:NF \ekvletunknownNoVal { \cs_new_protected:Npn \ekvletunknownNoVal #1 {\expandafter\let\csname\ekv@name{#1}{}uN\endcsname} } \exp_args:Nnc \ekvletunknownNoVal { \token_to_str:N \__emoro_colloop:nnn } { \ekv@name { \token_to_str:N \__emoro_colloop:nnn } { start } } \makeatother \cs_new:Npn \__emoro_colloop:nnNnn #1#2#3#4#5 { \emoro_int_step_tokens:nnnn {#1} {#2} {#4} { \etl_token_replace_all_deep:nNn {#5} #3 } } \msg_new:nnn { emoro } { zero-step } { Zero~ step~ size } \cs_new:Npn \emoro_int_step_tokens:nnnn #1#2#3#4 { \int_compare:nNnTF {#2} > \c_zero_int { \__emoro_int_step_tokens:Nfffn > } { \int_compare:nNnTF {#2} = \c_zero_int { \msg_expandable_error:nn { emoro } { zero-step } \prg_break: } { \__emoro_int_step_tokens:Nfffn < } } { \int_eval:n {#1} } { \int_eval:n {#2} } { \int_eval:n {#3} } {#4} \prg_break_point: } \cs_new:Npn \__emoro_int_step_tokens:Nnnnn #1#2#3#4#5 { \if_int_compare:w #2 #1 #4 \exp_stop_f: \prg_break:n \fi: \use:n {#5} {#2} \exp_args:NNf \__emoro_int_step_tokens:Nnnnn #1 { \int_eval:n { #2 + #3 } } {#3} {#4} {#5} } \cs_generate_variant:Nn \__emoro_int_step_tokens:Nnnnn { Nfff } \ExplSyntaxOff \begin{document} \begin{tabular}{lllll} \colloop{5}{column #} \\ \colloop[11]{15}{\multicolumn{1}{c}{c#}} \\ \colloop[s=2]{5} {% \ifnum#=1 \multicolumn{1}{r}{hihi1}% \else \multicolumn{2}{r}{hihi#}% \fi } \\ \colloop[5,s=-1]{1}{and #} \end{tabular} \end{document}