# Formatting output for \newcommand with for loop

I have the following minimum example

\documentclass[12pt]{article}
\usepackage{pgffor}

\newcommand{\formatlist}[2]{ {x^#1} \foreach \x in {#2} {,x^\x}}
\begin{document}
$$\formatlist{5}{6,7}$$
\end{document}


The foreach loop creates what I want to output, however, in order to prevent having an extra comma somewhere, I had to incorporate a second argument to get the formatting right. Does someone know how to do this with a single argument? If there's a way to splice a given argument into two lists then this would be a simple fix, but I couldn't figure out how to do that.

Welcome to TeX.SE! \foreach allows you to see the count of an item, and \ifnum can be used to distinguish between comma and no comma.

\documentclass[12pt]{article}
\usepackage{pgffor}

\newcommand{\formatlist}[1]{\foreach \X [count=\Y] in {#1} {\ifnum\Y=1
x^\X
\else
,x^\X
\fi}}
\begin{document}
$\formatlist{5,6,7}$
\end{document}


• @ScottWhite You're welcome! Please consider trading $$...$$ for $...$.
– user121799
Feb 10 '19 at 22:14
• You can move x^\X outside the branch: \ifnum\Y=1\else,\fi x^\X Feb 11 '19 at 1:05
• @HenriMenke You are right, thanks! (However, the point of this answer was more to say there is the count option and \ifnum to distinguish the first element from the rest.)
– user121799
Feb 11 '19 at 1:10

You can accommodate both lists and ranges. I also added an optional argument to change the base.

The idea is that if the mandatory argument contains .., then it denotes a range. Otherwise it is supposed to be a list (with comma separators). In both cases the first term is detached and the others are printed with a comma before them.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\formatlist}{ O{x} >{\SplitArgument{1}{..}}m }
{
\scott_format_rangeorlist:nnn {#1} #2
}

\seq_new:N \l__scott_format_list_seq
\tl_new:N \l__scott_format_list_tl

\cs_new_protected:Nn \scott_format_rangeorlist:nnn
{
\tl_if_novalue:nTF { #3 }
{% no range, assume list of values
\scott_format_list:nn { #1 } { #2 }
}
{% range
\scott_format_range:nnn { #1 } { #2 } { #3 }
}
}

\cs_new_protected:Nn \scott_format_list:nn
{
\seq_set_from_clist:Nn \l__scott_format_list_seq { #2 }
\seq_pop_left:NN \l__scott_format_list_seq \l__scott_format_list_tl
% print the first term
#1^{\l__scott_format_list_tl}
% print the other terms, with a comma
\seq_map_inline:Nn \l__scott_format_list_seq { , #1^{##1} }
}

\cs_new_protected:Nn \scott_format_range:nnn
{
#1^{#2}
\int_step_inline:nnn { #2 + 1 } { #3 } { ,#1^{##1} }
}
\ExplSyntaxOff

\begin{document}

\textbf{Ranges}

$\formatlist{5..7}$

$\formatlist{1..10}$

$\formatlist[y]{3}$

$\formatlist[z]{1..5}$

$\formatlist{8..8}$

\textbf{Lists}

$\formatlist{2,3,4,6}$

$\formatlist{2}$

$\formatlist[y]{6,8,10}$

\end{document}


1. The command \formatlist is declared to have an optional argument, with default value x, and a mandatory argument; due to the “preprocessor” \SplitArgument{1}{..}, this argument will be returned in the format {<A>}{<B>}, where <A> represents what comes before .. and <B> what's after; in the case .. doesn't appear, <B> will be something that will make the conditional \if_novalue:nTF to return true.

2. Control is then passed to \scott_format_rangeorlist:nnn, which takes three arguments; #2 will consist of two braced items, as explained before.

3. \scott_format_rangeorlist:nnn examines the third argument; if it is the special item that makes \if_novalue:nTF to return true, the first two arguments are passed to \scott_format_list:nn (and #2 is so assumed to be a comma separated list), otherwise \scott_format_range:nnn is called.

4. \scott_format_list:nn changes the comma separated list into a sequence, whose left item is then detached for being processed on its own and stored in a token list variable; then this first item is passed as exponent to #1 (the base); next the sequence is mapped to print ,#1^{##1}. In this context, ##1 represents the current item in the sequence.

5. \scott_format_range:nnn first prints #1^{#2} (here #2 is the lower bound in the range); then it does a loop printing ,#1^{##1} starting from #2+1 up to #3; here ##1 is the current integer in the loop.

6. The function \seq_map_inline:Nn loops over the sequence, executing each time the second argument with ##1 representing the current item.

7. The function \int_step_inline:nnn does a loop with step 1 from the integer given as first argument up to the integer given as second argument; each time the third argument is executed, with ##1 representing the current value.

• Though the answer marmot gave was the type I was looking for with my simple case, as someone who also loves programming general functions, I love your solution as well! Though I'm new to this more advanced LaTeXing and so I'm not yet able to understand most of what's going on with your solution. That code would make for a great teaching example I think if you could add a few comments on the different commands you used in it to make it more understandable :) Feb 10 '19 at 22:28
• @ScottWhite I added some explanations. Feb 11 '19 at 0:19