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 '13 at 21:20
  • @Bakuriu: Note his solution braces the argument for \doticks... which is necessary. – Werner Nov 13 '13 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 '13 at 22:04
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 '13 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 '13 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 '13 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 '13 at 22:26
  • 2
    @Bakuriu \count255 is 4 tokens not two – David Carlisle Nov 14 '13 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 '13 at 11:38
  • @jfbu I used Knuth style tail recursion on purpose. – egreg Nov 14 '13 at 11:51
  • I see, I thought \expandafter\stuff\fi was also called tail recursion. Should I delete my comment? – user4686 Nov 14 '13 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 '13 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 '13 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 – David Carlisle Nov 14 '13 at 11:17
  • @egreg \the is quicker than \number when prefixing a \numexpr. – user4686 Nov 14 '13 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 '13 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. – David Carlisle Nov 14 '13 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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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