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}

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.
Control is then passed to \scott_format_rangeorlist:nnn, which takes three arguments; #2 will consist of two braced items, as explained before.
\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.
\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.
\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.
The function \seq_map_inline:Nn loops over the sequence, executing each time the second argument with ##1 representing the current item.
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.
$$ ... $$for\[ ... \]. – Feb 10 '19 at 22:14x^\Xoutside the branch:\ifnum\Y=1\else,\fi x^\X– Henri Menke Feb 11 '19 at 01:05countoption and\ifnumto distinguish the first element from the rest.) – Feb 11 '19 at 01:10