# Making a range macro

Having syntactic sugar for ranges can be useful in programming languages. For example in Ruby, one can use a range by

(2..10).map {|elem| elem + 1}


to get an array consisting of each element in the range added by one ([3, 4, ..., 11]).

I've found it helpful to take a look at how things are done in other languages which use Alonzo Church's lambda logic, in this case, Scheme:

(define (range low high)
(cond
[(> low high) null]
[else (cons low (range (+ low 1) high))]))


(I'm assuming that [...]-syntax is specific to PLT Scheme. The above is taken from this webpage.)

So, recursion, eh? Sure, why not:

\def\range#1#2{\ifnum#1>#2\else#1, \range{\the\numexpr#1+1\relax}{#2}\fi}
\range{2}{10}
\bye


Well OK, I get the range (with a surplus comma at the end), but it starts to choke on larger ranges. Why? Looking at the log-file might give a clue with \tracingmacros=2:

\range #1#2-><macro definition>
#1<-\the \numexpr \the \numexpr \the \numexpr \the \numexpr ...
#2<-10


OK, I get the idea.

So I'd need to throw in some \expandafters, right?:

\catcode@=11

\def\R@nge#1#2{\number#1 \ifnum#1<#2, \expandafter\R@nge
\expandafter{\number\numexpr#1+1\expandafter}%
\expandafter{\number#2\expandafter}\fi}

\R@nge{2}{10}
\bye


Now it doesn't choke.

I've fallen in love with Alan Jeffrey's lambda.sty for lists in TeX's mouth, and so my instinct was to try and make that range into a list:

\catcode@=11
\input lambda.sty
\def\R@nge#1#2{\number#1 \ifnum#1<#2, \expandafter\R@nge
\expandafter{\number\numexpr#1+1\expandafter}%
\expandafter{\number#2\expandafter}\fi}

\Unlistize{\Listize[\R@nge{2}{10}]} % works, but getting back to the ruby example,

\def\Succ#1{\the\numexpr#1+1\relax}

\Unlistize{\Map\Succ{\Listize[\R@nge{2}{10}]}} % doesn't work
% (expected result: [3, 4, 5, 6, 7, 8, 9, 10, 11],
% instead of: [2, 3, 4, 5, 6, 7, 8, 9, 10+1].)
\bye


Maybe those \expandafters are getting back to haunt me at this stage?

So next I tried to follow more strictly the Scheme version:

\catcode@=11
\input lambda.sty
\def\Range#1#2{\TeXif{\ifnum#1>#2}{\Nil}%
{\Cons{#1}{\Range{\number\numexpr#1+1}{#2}}}}

\Unlistize{\Range{2}{10}}
\bye


Looks about the same as the Scheme version, doesn't it? Alas, it returns an empty list!

## The Question

How can I make a range-macro which would output a list (in the lambda.sty-sense) so I can use \Map and such with it?

I have an inkling that the command should somehow resemble the \Listize-macro from lambda.sty:

\def\Listize[#1]%
{\Listize@#1,\relax]}
\def\Listize@#1,#2]%
{\TeXif{\ifx\relax#2}%
{\Singleton{#1}}%
{\Cons{#1}{\Listize@#2]}}}


But I haven't been able to figure out what to change and how.

• Sorry, but I can't follow all the code snippets; please try and make them full compilable examples. Jun 24 '12 at 9:56
• @egreg: OK, done. Jun 24 '12 at 10:10

Beware (not) terminating numbers!

\catcode@=11
\input lambda.sty
\def\Range[#1..#2]{\TeXif{\ifnum#1>#2 }{\Nil}%
{\Cons{#1}{\expandafter\Range\expandafter[\number\numexpr#1+1\relax..#2]}}}

\immediate\write20{\Unlistize{\Range[2..10]}}

\bye


I added a space after #2 and a \relax after the \numexpr

\$ pdftex ltest2
This is pdfTeX, Version 3.1415926-2.3-1.40.12 (TeX Live 2011/Cygwin)
restricted \write18 enabled.
entering extended mode
(./ltest2.tex (./lambda.sty)
[2, 3, 4, 5, 6, 7, 8, 9, 10]
)
No pages of output.
Transcript written on ltest2.log.

• Awesome! Yes, that was IT! Thank you thank you thank you! :-D Jun 25 '12 at 10:53
• Ooh, just a short comment for the reader who is wondering about the \expandafter-connection on the "choking-effect": Just put a \expandafter before the recursive \Range call, and another after it. Makes all the difference. :-) Jun 26 '12 at 15:13
• oops sorry should have noticed that: feel free to edit my answer to your final tested version:-) Jun 26 '12 at 15:28