TransWikia.com

Store the expanded version of a macro into another macro in the plain format

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?

4 Answers

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

enter image description here

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

enter image description here

If one wishes to quibble over the fact that I build up the stack with unresolved fis, 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

  • Performs an assignment
  • Does any typesetting

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

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP