TeX - LaTeX Asked on June 4, 2021
I wrote the following macro that prints a range of numbers separated by commas:
newcounttmpnum
defrange#1#2#3{tmpnum=#2
loopthetmpnumadvancetmpnum by #1
ifnumtmpnum<#3,repeat,
}
For instance, range{2}{3}{10}
expands to 3,5,7,9,
.
I’d like to store this expansion into the macro therange
. What I tried is:
edeftherange{range{2}{3}{10}}
% Should be the same as
% deftherange{3,5,7,9,}
but I get an error message. Why? And how to achieve my goal?
The edef
command doesn't perform assignments and your loop is full of them.
With luatex
you can use expl3
.
input expl3-generic
ExplSyntaxOn
cs_new:Npn range #1 #2 #3
{% #1 is the step, #2 the starting point, #3 the upper bound
#2
int_step_function:nnnN { #2 + #1 } { #1 } { #3 } user_addtorange:n
}
cs_new:Nn user_addtorange:n { , #1 }
ExplSyntaxOff
range{2}{3}{10}
edeftherange{range{2}{3}{10}}
{ttmeaningtherange}
bye
The function int_step_function:nnnN
has the syntax
int_step_function:nnnN { <start> } { <step> } { <end> } <function>
where <function>
should be a one argument function (macro, in plain TeX lingo) which will be passed all the integers that result by looping in the obvious way. Only integers that don't exceed <end>
are passed. The function will expand to ,<current integer>
. The start is added beforehand, so we have no problem with spurious commas.
A version without expl3
, just for fun.
defrange#1#2#3{% #1 = step, #2 = start, #3 = upper bound
betterrange{#2}{#3}{#1}%
}
defbetterrange#1#2#3{% #1 = start, #2 = upper bound, #3 = step
#1%
ifnumnumexpr#1+#3>numexpr#2relax
expandaftergobble
else
expandafterfirstofone
fi
{expandafter,expandafterbetterrangeexpandafter{thenumexpr#1+#3}{#2}{#3}}%
}
defgobble#1{}
deffirstofone#1{#1}
range{2}{3}{10}
edeftherange{range{2}{3}{10000}}
showtherange
bye
Answered by egreg on June 4, 2021
defrange#1#2#3{altrange{#2}{#3}{#1}}
defaltrange#1#2#3{ifnum #1<numexpr 1+#2relax#1,expandafter
altrangeexpandafter{thenumexpr#1+#3relax}{#2}{#3}fi}
edeftherange{range{2}{3}{10}}
edefz{range{3}{3}{15}}
The range is therange while z is z
bye
If one wishes to quibble over the fact that I build up the stack with unresolved fi
s, until the end (which could only become a factor for very large lists), then one can play the "Free-fi
-Fo'-Fun" game that I learned from David (Trying to eliminate stack overflow during recursion (Alphabetic Bubble Sorter)):
defrange#1#2#3{ifx11altrange{#2}{#3}{#1}fi}
defaltrange#1#2#3fi{fiifnum #1<numexpr 1+#2relax#1,expandafter
altrangeexpandafter{thenumexpr#1+#3relax}{#2}#3fi}
edeftherange{range{2}{3}{10}}
edefz{range{3}{3}{15}}
The range is therange while z is z
bye
Answered by Steven B. Segletes on June 4, 2021
Other answers have shown ways to do this, I'd like to cover why your approach fails.
Only some TeX primitives work purely by expansion - that is to say that they can achieve their outcome inside an edef
, message
or similar. In particular, nothing that
works by expansion. The definition of loop
in plain TeX uses an assignment, and so cannot be used in such a context. There are ways do set up loops without assignment, as shown in other answers.
I note you are using LuaTeX: it's important to note that assignment at the Lua level is permitted, and so the 'rules are different' if you write code in Lua.
Answered by Joseph Wright on June 4, 2021
This is typical question for expandable loop. Various solutions were shown here. I show another solution using expandable fornumstep
from OpTeX (luaTeX + enhanced plain TeX):
defrange#1#2#3{fornumstep #1: #2..#3 do{##1,}}
%test:
edeftherange{range{2}{3}{10}}
meaningtherange
bye
Answered by wipet on June 4, 2021
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP