How to make a pipe-divided tuple?

I don't think you need a loop:

enter image description here

\documentclass{article}

\begingroup
\lccode`\~=`\|
\lowercase{\endgroup
\def\tup#1{{\def~{\;\middle\vert\;}\mathcode`\|="8000\left\langle#1\right\rangle}}}

\begin{document}

\[
\tup{a | b | c | z} + \tup{a |\frac{A}{B} | c | z}
\]

\end{document}

As a general rule, I recommend to avoid \left and \right whenever possible.

Here's an implementation with a syntax similar to commands defined with \DeclarePairedDelimiter from mathtools.

The unadorned command uses normal size; in the optional argument \big, \Big, \bigg or \Bigg can appear. The * means using automatic sizing.

Spaces in the mandatory argument are ignored, so \tup{a|b|c} is the same as \tup{a | b | c}.

\documentclass{article}
\usepackage{amsmath}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\tup}{som}
 {
  \IfBooleanTF{#1}
   {
    \tl_set:Nn \l__reinking_tup_open_tl { \left\langle }
    \tl_set:Nn \l__reinking_tup_middle_tl { \;\middle|\; }
    \tl_set:Nn \l__reinking_tup_close_tl { \right\rangle }
   }
   {
    \IfNoValueTF{#2}
     {
      \tl_set:Nn \l__reinking_tup_open_tl { \langle }
      \tl_set:Nn \l__reinking_tup_middle_tl { \mathrel{|} }
      \tl_set:Nn \l__reinking_tup_close_tl { \rangle }
     }
     {
      \tl_set:Nn \l__reinking_tup_open_tl { \mathopen{#2\langle} }
      \tl_set:Nn \l__reinking_tup_middle_tl { \mathrel{#2|} }
      \tl_set:Nn \l__reinking_tup_close_tl { \mathclose{#2\rangle} }
     }
   }
  \__reinking_tup:n { #3 }
 }

\tl_new:N \l__reinking_tup_open_tl
\tl_new:N \l__reinking_tup_middle_tl
\tl_new:N \l__reinking_tup_close_tl
\seq_new:N \l__reinking_tup_items_seq

\cs_new_protected:Nn \__reinking_tup:n
 {
  \seq_set_split:Nnn \l__reinking_tup_items_seq { | } { #1 }
  \l__reinking_tup_open_tl
  \seq_use:NV \l__reinking_tup_items_seq \l__reinking_tup_middle_tl
  \l__reinking_tup_close_tl
 }
\cs_generate_variant:Nn \seq_use:Nn { NV }

\ExplSyntaxOff

\begin{document}

$\tup{a|b|\dots|z}$
\quad
$\tup[\big]{a|b|\dots|z}$
\quad
$\tup[\Big]{a|b|\dots|z}$
\quad
$\tup*{\dfrac{a}{2}|b|\dots|z}$

\end{document}

enter image description here

A package-free version, which indiscriminately uses \left, \middle and \right. The items are absorbed one at a time and appended to a list, then the list is executed.

\documentclass{article}
\usepackage{amsmath}

\makeatletter
\newcommand{\tup}[1]{%
  \def\@tup@list{}%
  \@tup#1|\@tup|
}
\def\@tup#1|{%
  \ifx\@tup#1\relax
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
  {\left\langle\@tup@list\right\rangle}%
  {\ifx\@tup@list\@empty\@tup@append{#1}\else\@tup@append{\;\middle|\;#1}\fi\@tup}%
}
\def\@tup@append#1{%
  \expandafter\def\expandafter\@tup@list\expandafter{\@tup@list#1}%
}
\makeatother

\begin{document}

$\tup{a}$

$\tup{a|b|\dots|z}$

\end{document}

This way there's no problem with groups implicitly formed by \left, \middle and \right.


Your command was almost working. If you put more items in \tup, like $\tup{a|b|c|d|e|f}$ you'd see this:

enter image description here

it's not the first item which is left out, rather they are grouped in pairs. Your command starts with a \left\langle, which starts a "math left group" and with the \@tempswa switch set to false. In the first iteration of \@tuploop you do \if@tempswa\@sep\else\@tempswatrue\fi which expands to \@tempswatrue, meaning that no \@sep is inserted now, and \@tempswa is now true. The command then inserts the first item (a) and proceeds to the next iteration.

On the second iteration, \@tempswa is true and \if@tempswa\@sep\else\@tempswatrue\fi expands to \@sep, which inserts a \middle| (the separator after a). The \middle primitive will end the "math left group" started by \left\langle and will start another "math left group". However, when the first group ended, your \@tempswa switch was restored to its value outside the current group, which is false! And now the command inserts b and goes to the third iteration, but now with \@tempswa set to false once again.

So your command was actually inserting <token>|<token> and then <token>|<token>, and so on, due to the end of group triggered by \middle. You could circumvent this with some \aftergroup magic, or simply making a gloabl assignment to the switch:

\if@tempswa\@sep\else\global\@tempswatrue\fi

However, as egreg said in the comment, \@tempswa is supposed to be set locally only, so it would be better if you created a \newif\ifg@insertsep and then always used \global\g@insertseptrue and \global\g@insertsepfalse.

And since meanwhile egreg posted a much more elaborate expl3 answer than the one-liner I would post, I'll stop here ;-)