6

What's the most simple way, in plain TeX, to define a \macro that can be used as:

\macro{10}

\macro{\mycount}

such that I can then assign the number to a \count and manipulate it?

Note:

\def\macro#1{...\number#1 ...}

is not what I want.

In other words, how can I modify the following TeX code (see ex 10.4 of The TeXbook):

\def\tick#1{\vrule height 0pt depth #1pt}

\def\tmp{\hbox to 1cm{\hfil\tick4\hfil\tick8}}

\def\doticks#1{\count255=#1%
\ifnum\count255=1%
    \tmp%
\else%
    \advance \count255 by -1\relax%
    \tmp\doticks\count255%
\fi}

\def\ruler#1{\vbox{\hrule\hbox{\tick8\doticks\number#1}}}

In such a way that:

\ruler{10}

\count255=10
\ruler{\count255}
\bye

outputs a page with two 10 centimetres rulers drawn on it?

(current code gives a Missing number error).

3
  • I was using \count255 because the TeXbook mentions that it is a scratch register. However I just tried Ian solution and replacing \mycount with \count255 it doesn't work, so some macro in there is using \count255 too...
    – Bakuriu
    Nov 13, 2013 at 21:20
  • @Bakuriu: Note his solution braces the argument for \doticks... which is necessary.
    – Werner
    Nov 13, 2013 at 21:20
  • @Bakuriu, after adding the braces (\doticks{\number#1}), there is also the % in \def\doticks#1{\count255=#1% which is problematic, because the \ifnum test may be expanded before the assignment is done. If you do \expandafter\doticks\expandafter{\number#1} on one hand and on the other hand \def\doticks#1{\count255=#1 without a % (but with a space), the it will be ok. See my upcoming answer for the complete thing (there are other modifications too)
    – user4686
    Nov 13, 2013 at 22:04

4 Answers 4

4

In addition to @Werner's suggestion of using your own counter, I think you need braces around \number#1, otherwise \doticks picks up \number as its argument.

\def\tick#1{\vrule height 0pt depth #1pt}

\newcount\mycount
\def\tmp{\hbox to 1cm{\hfil\tick4\hfil\tick8}}

\def\doticks#1{\mycount=#1%
\ifnum\mycount=1%
    \tmp%
\else%
    \advance \mycount by -1\relax%
    \tmp\doticks\mycount%
\fi}

\def\ruler#1{\vbox{\hrule\hbox{\tick8\doticks{\number#1}}}} % <--- new braces here

\ruler{10}

\mycount=10
\ruler{\mycount}
\bye
6
  • Damn, I lost hours for this and turns out I forgot one pair of braces. I must mention that the use of \newcount seems required. If I copy-paste your solution and replace \mycount with \count255 inside the \doticks macro I still get a Missing number ... error. Next time I'll make sure to avoid even so-called scratch registers...
    – Bakuriu
    Nov 13, 2013 at 21:24
  • 1
    If you wish to use \count255, you need to enclose it in braces whenever it is passed to \doticks (there are two locations).
    – Werner
    Nov 13, 2013 at 21:25
  • @Werner Right. I'm probably too affected by programming languages where identifiers are single tokens and can contain digits, while \count255 contains two tokens... From now on until I learn I should really first enclose any argument with braces, and afterwards ask my self if they contain a single token or not and eventually remove them. Maybe doing this I'll finally learn.
    – Bakuriu
    Nov 13, 2013 at 21:52
  • 2
    All % in the definition of \doticks are either wrong or redundant. Precisely, the first should be \relax, the second should be removed and the others are redundant.
    – egreg
    Nov 13, 2013 at 22:26
  • 2
    @Bakuriu \count255 is 4 tokens not two Nov 14, 2013 at 1:32
6

Use \afterassignment and a group, so not to clobber the value of \count255. Also use tail recursion.

\def\tick#1{\vrule height 0pt depth #1pt }

\def\tmp{\hbox to 1cm{\hfil\tick4\hfil\tick8}}

\def\doticks#1{\begingroup\afterassignment\dodoticks\count255=#1\endgroup}
\def\dodoticks{%
  \tmp
  \ifnum\count255=1
    \let\next\relax
  \else
    \advance \count255 by -1
    \let\next\dodoticks
  \fi
  \next
}

\def\ruler#1{\vbox{\hrule\hbox{\tick8\doticks{#1}}}}

\ruler{10}

\count255=10
\ruler{\count255}

\bigskip

{\tt\string\count255} is \number\count255

\bye

enter image description here

The same with some @'s:

\def\tick#1{\vrule height 0pt depth #1pt }

\def\tmp{\hbox to 1cm{\hfil\tick4\hfil\tick8}}

\catcode`@=11
\def\doticks#1{\begingroup\afterassignment\d@ticks\count@=#1\endgroup}
\def\d@ticks{%
  \tmp
  \ifnum\count@=\@ne
    \let\next\relax
  \else
    \advance \count255 by \m@ne
    \let\next\d@ticks
  \fi
  \next
}
\catcode`@=12
\def\ruler#1{\vbox{\hrule\hbox{\tick8\doticks{#1}}}}

\ruler{10}

\count255=10
\ruler{\count255}

\bigskip

{\tt\string\count255} is \number\count255

\bye
4
  • you can optimize your \d@ticks via \ifnum\count@=\@ne\else\advance\count@\m@ne\expandafter\d@ticks\fi (or \ifnum\count@>\@ne etc... which perhaps is faster than testing for equality and then \else. I don't know)
    – user4686
    Nov 14, 2013 at 11:38
  • @jfbu I used Knuth style tail recursion on purpose.
    – egreg
    Nov 14, 2013 at 11:51
  • I see, I thought \expandafter\stuff\fi was also called tail recursion. Should I delete my comment?
    – user4686
    Nov 14, 2013 at 12:12
  • @jfbu It is tail recursion: the important thing is that the conditional is removed from the input stream and both methods do it. Actually, removing \let\next\relax and changing \let\next\d@ticks into \expandafter\d@ticks should be more efficient. As I said, I just wanted to do it Knuth-style.
    – egreg
    Nov 14, 2013 at 12:18
6

A version without using a temp count register. Requires etex but pdftex includes that by default.

\def\tick#1{\vrule height 0pt depth #1pt}



\def\doticks #1{%
 \hbox to 1cm{\hfil\tick4\hfil\tick8}%
\ifnum#1>0
  \expandafter\doticks\expandafter{\the\numexpr#1-1\expandafter\relax\expandafter}%
\fi
}

\def\ruler#1{\vbox{\hrule\hbox{\tick8\doticks{#1}}}}

\ruler{10}

\newcount\mycnt

\mycnt=10

\ruler{\mycnt}

\nopagenumbers
\bye
5
  • I'm pretty sure \numexpr is not expandable, so unless you add \number in front of it the first two \expandafter tokens do nothing.
    – egreg
    Nov 14, 2013 at 10:40
  • @egreg, they did something when they were written (as I had \the) seems like I took that out before posting, but left the expandafter:( will fix Nov 14, 2013 at 11:17
  • @egreg \the is quicker than \number when prefixing a \numexpr.
    – user4686
    Nov 14, 2013 at 11:24
  • @DavidCarlisle you don't need the \relax, the closing brace will stop the \numexpr, (after the \expandafter has taken care of the fi).
    – user4686
    Nov 14, 2013 at 11:26
  • @jfbu yes I know but it's easier to put it there than worry about not needing it (it would be needed if the outer exandafters were removed as then the {} would be macro delimiters and be gone by the time numexpr was evaluated. Nov 14, 2013 at 16:35
5

You can also do it in the following (not very efficient!) style:

\def\tick#1{\vrule height 0pt depth #1pt}

\def\tmp{\hbox to 1cm{\hfil\tick4\hfil\tick8}}

\def\doticks#1{\count255=#1\relax
\ifnum\count255=1
    \tmp
\else
    \advance \count255 by -1
    \tmp\doticks{\count255 }%
\fi}

\def\ruler#1{\vbox{\hrule\hbox{\tick8\doticks{#1}}}}

\ruler{10}

\count255=5

\ruler{\count255}

\nopagenumbers
\bye

rulers

Actually, this code (maybe I should have read it) indeed does nest many \if..\fi so to avoid that, one could do the (somewhat silly but working):

\def\doticks#1{\count255=#1\relax
\tmp
\ifnum\count255=1 
      \let\next\relax
\else
    \def\next {\doticks{\count255 }}%
    \advance \count255 by -1
\fi
\next }

Anyway, there is in the Plain format a loop which it is very natural to use here:

\def\tick#1{\vrule height 0pt depth #1pt}

\def\tmp{\hbox to 1cm{\hfil\tick4\hfil\tick8}}

\def\doticks #1{\count255=#1\relax
\loop 
  \tmp
\ifnum\count255>1 
  \advance\count255 by -1
\repeat
}

\def\ruler#1{\vbox{\hrule\hbox{\tick8\doticks{#1}}}}

\ruler{10}

\newcount\mycnt

\mycnt=10

\ruler{\mycnt}

\nopagenumbers
\bye

rulers

If one wants to preserve as egreg the value stored in \count 255 one can also do it without creating a group. It is irrelevant for the task at hand, but for other situations it could be useful to not create a group.

(for efficiency one should also do as egreg and use \count@, @ne, m@ne etc...)

\def\tick#1{\vrule height 0pt depth #1pt}

\def\tmp{\hbox to 1cm{\hfil\tick4\hfil\tick8}}

\def\doticks #1{\edef\restorecountxxlv{\count255=\the\count255\relax}%
                \count255=#1\relax \dotickticks \restorecountxxlv }

\def\dotickticks{\tmp
                 \ifnum\count255>1 
                   \advance\count255 by -1 
                   \expandafter\dotickticks
                 \fi }

\def\ruler#1{\vbox{\hrule\hbox{\tick8\doticks{#1}}}}

\count255 7

\ruler{10}

{\tt\string\count 255} stores \the\count255

\newcount\cnt
\cnt=3

\ruler{\cnt}

{\tt\string\count 255} stores \the\count255

\bye

rulers rule

0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .