Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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.

share|improve this question
    
Sorry, but I can't follow all the code snippets; please try and make them full compilable examples. –  egreg Jun 24 '12 at 9:56
    
@egreg: OK, done. –  morbusg Jun 24 '12 at 10:10
add comment

1 Answer

up vote 5 down vote accepted

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.
share|improve this answer
    
Awesome! Yes, that was IT! Thank you thank you thank you! :-D –  morbusg 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. :-) –  morbusg Jun 26 '12 at 15:13
    
oops sorry should have noticed that: feel free to edit my answer to your final tested version:-) –  David Carlisle Jun 26 '12 at 15:28
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.