You said:
...so apologies if the more experienced of you out can't help but laugh at this code.
When I started learning TeX, I had the feeling that it was a very steep learning curve.
From time to time I was frustrated. ;-)
I do not believe that people like you, who are taking on this learning curve, are in a situation where it is appropriate to laugh at their attempts at macro programming/TeX-programming.
I think any attempt to achieve or learn something good in a way which both is also based on reason and in itself is no misdeed, basically deserves respect.
If you have questions about how the code from my examples below works, don't hesitate to ask them. It is then useful to describe how you think the code works and where you get stuck with the understanding. In my experience, this makes it easier for the respondents to find out exactly what information (e.g. about how TeX primitives work and which of the "side effects" briefly hinted at in the back chapters of the TeXbook are used for programming tricks) is still missing for understanding.
Assuming that the entries in your comma-list are not surrounded by spaces, and that the \relax-primitive does not occur within the comma-list, and that \numexpr from the ε-TeX-extensions is available, you can probably do something like this:
\long\def\gobble#1{}%
\long\def\firstofone#1{#1}%
\def\Iterator#1#2,{%
% #1 - element-separator to prepend; empty in the 1st iteration;
% comma in consecutive iterations
% #2 - either current element of old list or the \relax that was
% appended for denoting the end of the list
\ifx\relax#2\expandafter\gobble\else\expandafter\firstofone\fi
{%
#1\number\numexpr#2+1\relax\Iterator{,}%
}%
}%
\def\MyList{1,2,3}
\edef\newList{\expandafter\Iterator\expandafter{\expandafter}\MyList,\relax,}
\begingroup
\tt
\string\MyList: \meaning\MyList
\string\newList: \meaning\newList
\endgroup
\bye
The gist with this example is:
Within the \edef-definition-text of \newList the TeX-primitive \expandafter is used for expanding \MyList. Also the sequence ,\relax, is appended.
This way at the time of defining \newList \edef-driven expansion of the definition-text of \newList at some stage yields a sequence \Iterator{}Comma,sparated,items,from,\MyList,\relax,.
So \relax, marks the end of the list.
Now—still driven by \edef-expansion—\Iterator (recursively) picks a non-delimited-argument #1 (which in the first iteration is empty and in consecutive iterations holds a comma, i.e., which holds the separator to prepend to the item of the new list) and a comma-delimited-argument #2 which either holds the next item coming from \myList's comma-list or holds the end-marker \relax , and in any case places—nested in curly braces— a token-sequence denoting the next iteration, formed by
- the undelimited-argument
#1, i.e., the separator that must precede the next item of the new list,
- the expression
\number\numexpr#2+1\relax for adding 1 to the value represented by the comma-delimited-argument and this way forming the next item of the new list,
- a call to itself for processing the next item remaining from the expansion of
\myList, this time providing a comma within the undelimited argument, denoting that next time the next item of the new list is to be preceded by a comma.
Via \ifx\relax#2 it is checked whether the end of the comma-list/the \relax appended to the list in the beginning of \edef-expansion is reached. If so, the token-sequence nested in curly braces denoting the next iteration is "gobbled/removed" via \gobble and thus not carried out, which terminates iteration/tail-recursion. If not so, the surrounding curly braces are removed from that sequence by applying \firstofone whereafter that sequence gets processed.
The undelimited argument #1 of \Iterator, which holds the separator to prepend to the item of the new list, only in the first iteration is empty. In each consecutive iteration it holds a comma which in that consecutive iteration's previous iteration was provided by the \Iterator-macro itself as a part of the token-sequence which then formed the next iteration. This way (only) the first item of the new list is not preceded by a comma.
If you don't have ε-TeX-extensions' \numexpr available I can offer a routine for incrementing non-negative integer numbers. (In "real life" you might be interested in the packages intcalc and bigintcalc.)
%------------------------------------------------------------------------------
% Expandable incrementing of non-negative integer number formed by a sequence
% of explicit catcode-12-character-tokens from the set {0,1,2,3,4,5,6,7,8,9}
%..............................................................................
% \Increment{<non-negative integer number k as sequence of explicit
% catcode-12-character-tokens from the set 0123456789>}
% ->
% <natural number (k+1) as sequence of explicit catcode-12-character-tokens
% from the set 0123456789>
% In expansion-contexts the result is delivered after two expansion-steps/is
% obtained by "hitting" \Increment with \expandafter twice.
%------------------------------------------------------------------------------
\def\Increment#1{%
\romannumeral0%
\IncrementReverse{\IncrementFork{}}{\relax}{}#1\relax
}%
\def\IncrementReverse#1#2#3#4{%
% #1 - tokens to prepend to reversed list
% #2 - tokens to append to reversed list
% #3 - reversed list constructed so far
% #4 - current element of not-reversed list
\ifx\relax#4%
\expandafter\firstoftwo
\else
\expandafter\secondoftwo
\fi
{#1#3#2}{\IncrementReverse{#1}{#2}{#4#3}}%
}%
\def\IncrementSelect#10123456789\relax#2#3!!{#2}%
\def\IncrementFork#1#2{%
% #1 - digits incremented so far
% #2 - current digit to increment or end-marker \relax
\IncrementSelect
#2123456789\relax{\IncrementReverse{ }{}{}#11}%
0#223456789\relax{\IncrementReverse{ }{}{}#12}%
01#23456789\relax{\IncrementReverse{ }{}{}#13}%
012#2456789\relax{\IncrementReverse{ }{}{}#14}%
0123#256789\relax{\IncrementReverse{ }{}{}#15}%
01234#26789\relax{\IncrementReverse{ }{}{}#16}%
012345#2789\relax{\IncrementReverse{ }{}{}#17}%
0123456#289\relax{\IncrementReverse{ }{}{}#18}%
01234567#29\relax{\IncrementReverse{ }{}{}#19}%
012345678#2\relax{\IncrementFork{#10}}%
0123456789#2{\IncrementReverse{ }{}{}#11\relax}%
0123456789\relax{\IncrementReverse{ }{}{}#11#2}%
!!%
}%
%%-----------------------------------------------------------------------------
\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\def\Iterator#1#2,{%
% #1 - element-separator to prepend
% #2 - current element of old list
\ifx\relax#2\expandafter\firstoftwo\else\expandafter\secondoftwo\fi
{}{%
#1\Increment{#2}\Iterator{,}%
}%
}%
\def\MyList{1,2,3}
\edef\newList{\expandafter\Iterator\expandafter{\expandafter}\MyList,\relax,}
\begingroup
\tt
\string\MyList: \meaning\MyList
\string\newList: \meaning\newList
\endgroup
\bye
If you wish a routine which does without \edef, you can, e.g., use the \romannumeral0-expansion- and argument-exchanging-technique—the gist of \romannumeral0-expansion is:
- TeX expands expandable tokens while gathering tokens that belong to the ⟨number⟩-quantity that is to be represented in roman numerals.
- If the first token which TeX finds while gathering the ⟨number⟩-quantity is a digit, e.g.,
0, then the process of gathering tokens that belong to the ⟨number⟩-quantity turns into a process of gathering more digits or something that is not a digit and therefore terminates the process of gathering. Expandable tokens get expanded while gathering digits. A space-token terminating a digit-sequence terminates the process of gathering more digits and gets silently discarded.
- If the number gathered is not positive, TeX will silently swallow the tokens forming the ⟨number⟩-quantity without delivering any token in return.
This implies that \romannumeral can be used for tricking TeX into doing a lot of expansion- and argument-exchanging-work as long as it is ensured that in the end a non-positive number is found.
%------------------------------------------------------------------------------
% Expandable incrementing of non-negative integer number formed by a sequence
% of explicit catcode-12-character-tokens from the set {0,1,2,3,4,5,6,7,8,9}
%..............................................................................
% \Increment{<non-negative integer number k as sequence of explicit
% catcode-12-character-tokens from the set 0123456789>}
% ->
% <natural number (k+1) as sequence of explicit catcode-12-character-tokens
% from the set 0123456789>
% In expansion-contexts the result is delivered after two expansion-steps/is
% obtained by "hitting" \Increment with \expandafter twice.
%------------------------------------------------------------------------------
\def\Increment#1{%
\romannumeral0%
\IncrementReverse{\IncrementFork{}}{\relax}{}#1\relax
}%
\def\IncrementReverse#1#2#3#4{%
% #1 - tokens to prepend to reversed list
% #2 - tokens to append to reversed list
% #3 - reversed list constructed so far
% #4 - current element of not-reversed list
\ifx\relax#4%
\expandafter\firstoftwo
\else
\expandafter\secondoftwo
\fi
{#1#3#2}{\IncrementReverse{#1}{#2}{#4#3}}%
}%
\def\IncrementSelect#10123456789\relax#2#3!!{#2}%
\def\IncrementFork#1#2{%
% #1 - digits incremented so far
% #2 - current digit to increment or end-marker \relax
\IncrementSelect
#2123456789\relax{\IncrementReverse{ }{}{}#11}%
0#223456789\relax{\IncrementReverse{ }{}{}#12}%
01#23456789\relax{\IncrementReverse{ }{}{}#13}%
012#2456789\relax{\IncrementReverse{ }{}{}#14}%
0123#256789\relax{\IncrementReverse{ }{}{}#15}%
01234#26789\relax{\IncrementReverse{ }{}{}#16}%
012345#2789\relax{\IncrementReverse{ }{}{}#17}%
0123456#289\relax{\IncrementReverse{ }{}{}#18}%
01234567#29\relax{\IncrementReverse{ }{}{}#19}%
012345678#2\relax{\IncrementFork{#10}}%
0123456789#2{\IncrementReverse{ }{}{}#11\relax}%
0123456789\relax{\IncrementReverse{ }{}{}#11#2}%
!!%
}%
%%-----------------------------------------------------------------------------
\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\long\def\exchange#1#2{#2#1}%
\def\Iterator#1,#2\relax#3#4{%
% #1 - current element of old list
% #2 - remaining elements of old list
% #3 - element-separator to prepend
% #4 - new list constructed so far
\ifx\relax#1\expandafter\firstoftwo\else\expandafter\secondoftwo\fi
{ #4}{%
\expandafter\exchange
\expandafter{%
\expandafter{%
\romannumeral0%
\expandafter\expandafter\expandafter\exchange
\expandafter\expandafter\expandafter{%
\Increment{#1}}{ #4#3}}}{\Iterator#2\relax{,}}%
}%
}%
\def\MyList{0,1,2,3}
\expandafter\def
\expandafter\newList
\expandafter{%
\romannumeral0\expandafter\Iterator\MyList,{\relax},\relax{}{}}%
\begingroup
\tt
\string\MyList: \meaning\MyList
\string\newList: \meaning\newList
\endgroup
\bye
\loopisn't built in it's just a recursive macro definition not so unlike this – David Carlisle Aug 27 '20 at 16:49123,456currently you would loop 7 iterations over that not two. or are you assuming that the input would be{123},{456}in that case (but then you can not useifxtests) – David Carlisle Aug 27 '20 at 16:52\expandafter\expandafter\expandafter\Iterator\fi\fi.– David Carlisle Aug 27 '20 at 16:54\expandafter\expandafter\expandafter\Iterator\fi\fimeans \expandafter{\expandafter{\expandafter....? – Harry Aug 27 '20 at 17:04\expandafter{means expandafter the{not expand after the whole group – David Carlisle Aug 27 '20 at 17:15+1math operation (as David's answer assumes) or are you merely looking to piece together string data, as in suffixing each element with a+1string (without mathematical evaluation)? – Steven B. Segletes Aug 27 '20 at 17:24\expandafter\Iterator\fiso then the remaining (middle) expandafter expands the remaining (second) \fi – David Carlisle Aug 27 '20 at 18:07\the\numexpr, you will get a string answer like1+1,2+1,3+1,25+1,.... – Steven B. Segletes Aug 29 '20 at 02:56...\ifx\myStop#1 %do nothing {end of list}...places a space-token (explicit character-token of character-code 32 and category-code 10(space)) behind#1. In horizontal mode, this space-token may yield horizontal glue. You probably better do...\ifx\myStop#1%<- space before the comment-char-removed; do nothing {end of list}...– Ulrich Diez Aug 29 '20 at 08:36... \ifx,#1 \expandafter\IteratorIntermediateOne %.... Better do:... \ifx,#1\expandafter\IteratorIntermediateOne %...Be aware that in any case this can be fooled by list-items which themselves contain leading commas, e. g., the second item{,2}of\edef\MyList{1,{,2},3}. – Ulrich Diez Aug 29 '20 at 08:41#define,#ifdefsystem, not to C. For a system with a conventional programming language under that macro layer look at luatex which has tex macro layer but also Lua. Also your aims are contradictory as you are asking for plain tex solutions, so that is like asking for C or Java with no libraries available and having to code everything by hand. look attexdoc interface3for a library of tex constructs for loops and tests and other programming things developed over 30 years. – David Carlisle Aug 29 '20 at 09:08