Completely expandable loop macro that also works in tabular

This loop just uses expansion. In order to use #1 as the loop counter it is easiest to define it anew each time, if you didn't want access to the counter you could more easily use \myrepeat{5}{some code} Note i didn't use \repeat as that name is taken in the existing \loop syntax.

\documentclass{article}

\def\myrepeat#1{\ifnum#1=0 \expandafter\stopmyrepeat\fi 
[This is iteration \number#1]
\expandafter\myrepeat\expandafter{\the\numexpr#1-1\relax}}

\def\stopmyrepeat#1\myrepeat#2#3{}

\begin{document}


\myrepeat{18}

\end{document}

There are a number of possible approaches to doing this without the complexity: I'll cover a couple using expl3. First, if you don't mind keeping things non-expandable then you could do something like

\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
\cs_new_protected:Npn \For #1#2#3#4
  {
    \int_step_inline:nnnn {#1} {#2} {#3 - 1} {#4}
  }
\cs_new_eq:NN \fpeval \fp_eval:n
\ExplSyntaxOff
\newcommand*\N{5}
\begin{document}

\begin{tabular}{|r|*{\N}{r|}}
    \hline
    $x$:   \For{0}{1}{\N}{&#1}\\
    \hline
    $e^x$: \For{0}{1}{\N}{& \fpeval{round(exp(#1),5)}}\\
    \hline
\end{tabular}
\end{document}

where the code to implement the cells can then be given at point-of-use. You could instead have a two-part setup where the functions are pre-coded: this is then expandable at point-of-use

\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
\cs_new_protected:Npn \For #1#2#3#4
  {
    \int_step_function:nnnN {#1} {#2} {#3 - 1} #4
  }
\cs_new_eq:NN \fpeval \fp_eval:n
\ExplSyntaxOff
\newcommand*\N{5}
\newcommand\CellNumber[1]{&#1}
\newcommand\CellExp[1]{&\fpeval{round(exp(#1),5)}}
\begin{document}
\begin{tabular}{|r|*{\N}{r|}}
    \hline
    $x$:   \For{0}{1}{\N}\CellNumber\\
    \hline
    $e^x$: \For{0}{1}{\N}\CellExp\\
    \hline
\end{tabular}
\end{document}

In both cases, the \int_step_... functions work much like David's answer: the loop itself is done by expansion. You can't do an assignment to \x or similar and have expandability.

An alternative approach if you are using LuaTeX is to do everything in Lua: the primitive here is expandable so you can use a perhaps more familiar approach (the question suggests a C background).

\documentclass{article}
\newcommand\For[4]{%
  \directlua{%
    for x = #1, (#3 - 1), #2 do
      tex.print(#4)
    end
  }%
}
\newcommand*\N{5}
\makeatletter
\let\Percent\@percentchar
\makeatother
\begin{document}

\begin{tabular}{|r|*{\N}{r|}}
    \hline
    $x$:   \For{0}{1}{\N}{"&" .. x}\\
    \hline
    $e^x$: \For{0}{1}{\N}{"&" .. string.format("\Percent.3f", math.exp(x))}\\
    \hline
\end{tabular}
\end{document}

For the number formatting, we need % which is a bit awkward in TeX: I've given a 'user' name to the LaTeX kernel command \@percentchar (a % which isn't a comment char).


Thanks to the inspiration from David's and Joseph's use of #1 as placeholder for the loop counter, I was able to improve the implementation that I've given in the question. I'll post it here mostly as a learning exercise for anyone who happens to have a similar use case. This version only expands the loop counter and leaves all other tokens alone, removing the need to use \noexpand to protect stuff that doesn't like being expanded like \pgfmath*.

The advantage of using a macro over Lua is in my opinion the easier specification of the loop body. With Lua one has to fight with tex.print and string.format and %-characters and stuff like that. In contrast, a macro version allows you to simply write the loop body using LaTeX code, exactly as you would write it manually without the loop.

An invocation of the loop now looks like this, where the name \i is only used internally (I left it there mostly because I like the syntax) and is not available inside the loop body. Instead #1 must be used to access the counter value:

\For(\i=0; \i<5; \i=\i+1) { Current value of i: #1 }

Of course it's still not expandable, but it seems to work amazingly well, and even nested loops are possible, using ##1 and ####1 as the nested counters. The only thing that didn't work in the quick test (see below) was using \multicolumn in a loop: It complains about Misplaced \omit., which seems to occur when there is 'stuff' before the \multicolumn command, though I couldn't figure out what exactly was the issue in this case.

Here's the compiled example, followed by the code that produced it.

Some tabulars that were created using loops

\documentclass{article}

% Some test data for \csvread
\begin{filecontents*}{test.csv}
1,2,3,4,5,6,7
4,5,6,7,8,9,0
\end{filecontents*}

% The actual loop package
\begin{filecontents*}{myforloop.sty}
\usepackage{pgf}
\usepackage{ifthen}

% Example (spaces optional):
% \For (\i = 0; \i < 10; \i = \i + 1) {
%   some code using #1
% }

\newtoks\For@R% Accumulator for tokens from all iterations
\newtoks\For@B% Loop body with #1 replaced by current counter value

\def\For(#1=#2;#3;#4=#5)#6{%
    \let\EA=\expandafter%                          For shorter writing ;-)
    \def\For@result{}%                             Macro to store the result
    \pgfmathtruncatemacro{#1}{#2}%                 Set initial value of counter
    \def\For@body##1{#6}%                          Loop body for replacing counter
    \def\For@inner{%                               Recursive loop macro
        \pgfmathparse{int(#3)}%                    Calculate loop condition
        \ifthenelse{1=\pgfmathresult\relax}{%      if loop condition is true:
            \For@R=\EA{\For@result}%                 Get current result tokens
            \For@B=\EA\EA\EA{\EA\For@body\EA{#1}}%   Replace *only* counter in body
            \edef\For@result{\the\For@R\the\For@B}%  Append current body to result
            \pgfmathtruncatemacro{#1}{#5}%           Set new counter value
            \For@inner%                              Recurse
        }{%                                        else:
            \For@result%                             Expand to result
        }%
    }%
    \For@inner%                                  Start looping
}
\end{filecontents*}

\usepackage{myforloop}
\usepackage{csvsimple}

\newcommand\N{4}% Number of loop iterations

\begin{document}
% Generate multiple columns in a single row
\begin{tabular}{|r|*{\N}{r|}}
    \hline
    $x$:   \For(\i=0; \i<\N; \i=\i+1) {&#1}\\\hline
    $e^x$: \For(\i=0; \i<\N; \i=\i+1) {& \pgfmathparse{exp(#1)}\pgfmathresult}\\\hline
\end{tabular}
\\[1em]

% Generate multiple rows with a single loop
\begin{tabular}{|l|l|}
    \hline
    $x$ & $e^x$ \\\hline
    \For(\i=0; \i<\N; \i=\i+1) {%
        #1 & \pgfmathparse{exp(#1)}\pgfmathresult \\\hline
    }
\end{tabular}
\\[1em]

% Generate a multi-dimensional table using nested loops
\begin{tabular}{l*{\N}{r}}
    \For(\k=0; \k<3; \k=\k+1) {%
        Group #1:\\
        \For(\i=0; \i<\N; \i=\i+1) {%
            Row ##1: \For(\j=0; \j<\N; \j=\j+1) {& #1,##1,####1} \\
        }
    }
\end{tabular}
\\[1em]

% Generate columns 2 to 7: \csvcoli to \csvcolvii:
\csvreader[tabular=*{8}{r}, no head]{test.csv}{}{
    \csvcoli \For(\i=2; \i<=7; \i=\i+1) {& \csname csvcol\romannumeral ##1\endcsname}
}
\\[1em]

% Some things do not work :-(
\begin{tabular}{lll}
    % Complains about `Misplaced \omit.`
    %\For(\k=0; \k<\N; \k=\k+1) {\multicolumn{3}{c}{center}}
\end{tabular}
\\[1em]

\end{document}

If you've read this far, you probably agree that there's still room for improvement in the current version. For example the two token registers and the \edef could probably be replaced by a single token register assignment. However I couldn't find the right sequence of \expandafters to expand the counter macro #1, followed by the loop body \For@body{counter-value}, followed by the current accumulator \the\For@R. The best I could think of was:

\For@R=\EA\EA\EA{\EA\the\EA\For@R\EA\For@body\EA{#1}}

but this hits some kind of internal TeX limit. And from my limited experience with the matter, it's probably not enough \EAs. ;-)