4

Currently, I’m in the process of writing a travel guide and I’d like to include general price information next to the locations and events, e.g.

$$$$ - Fancy Expensive Steakhouse

I figured that since I’d be doing this a lot it’d be a good idea to make a little command to insert an arbitrary number of dollar signs instead of individually escaping each dollar sign, but for the life of me I couldn’t figure it out.

Ideally, I’d want the command to go something like

\price{4}

To insert 4 dollar signs like in the above. I feel like I’m missing something simple, but after googling various variations of my question and looking into tex macros (unfortunately unfruitfully) I’m still at a loss. I’d also like to be able to format the dollar symbols (in gray for instance), but I feel like I can do that by defining an environment around the \price command once I get it working.

Any help or pointers to relative documentation would be greatly appreciated. Thanks!

3 Answers3

5

enter image description here

One way is to have a simple recursion on the number


\documentclass{article}
\begin{document}
\newcommand\price[1]{\ifnum#1>0\$\price{\numexpr#1-1\relax}\fi}

\price{1} zzz

\price{20} ZZZ

\end{document}

David Carlisle
  • 757,742
  • Darn. Beat me by 15 sec – Steven B. Segletes Sep 17 '21 at 16:10
  • 1
    @StevenB.Segletes and I printed more $ – David Carlisle Sep 17 '21 at 16:10
  • 1
    Surely, a bargain at any price! – Steven B. Segletes Sep 17 '21 at 16:11
  • Nice construction. Just to be sure, that is not tail recursion, isn't it? Then it ought to be something like \expandafter\price\fi? This macro will be building up on the stack? – Raoul Kessels Sep 17 '21 at 22:30
  • 2
    @RaoulKessels yes true but I figured you wouldn't have more that 10 or so iterations so the extra code to make it tail recursive would complicate the answer for no great benefit – David Carlisle Sep 17 '21 at 22:53
  • 1
    @RaoulKessels \newcommand\price[1]{\iftrue\priceaux#1\fi} \def\priceaux#1\fi{\fi\ifnum#1>0 \$\expandafter\priceaux\expandafter{\numexpr#1-1}\fi} Note the \expandafters are optional. The absorption of \fi by \priceaux makes it a "tail recursion" – Steven B. Segletes Sep 18 '21 at 01:22
  • @RaoulKessels I learned the \fi trick from an old-timer named...David Carlisle: https://tex.stackexchange.com/questions/372639/trying-to-eliminate-stack-overflow-during-recursion-alphabetic-bubble-sorter/372644#372644 – Steven B. Segletes Sep 18 '21 at 01:30
  • @DavidCarlisle I didn't mean that it was incorrect or something like that. I just wanted to make sure if I had understood the answers here: tex.stackexchange.com/questions/359189/looping-over-strings – Raoul Kessels Sep 18 '21 at 06:12
  • @StevenB.Segletes I knew it could be done ;-) Now I will have to study how it works. Thx – Raoul Kessels Sep 18 '21 at 06:14
  • @StevenB.Segletes The \expandafters "hitting" \numexpr have no effect because \numexpr is not expandable. \number, however, is expandable... – Ulrich Diez Sep 19 '21 at 01:30
  • @UlrichDiez Wow, that is news to me. Yet, \the\numexpr is expandable? If so, then I just need to add \the to my code – Steven B. Segletes Sep 19 '21 at 02:54
  • @RaoulKessels I was a bit shocked to discover from Ulrich that \numexpr is not expandable, even though I know \the\numexpr to be expandable. What this means for the code I provided in the earlier comment is that I should have the braced expression as {\the\numexpr#1-1} in order for the \expandafters to have an effect (or, as Ulrich suggested, {\number\numexpr#1-1}). – Steven B. Segletes Sep 19 '21 at 03:06
  • @StevenB.Segletes \the and \number are expandable. Thus you can do \the\numexpr... or \number\numexpr..., just as you like. With \dimexpr etc you need to use \the. – Ulrich Diez Sep 19 '21 at 16:26
  • @UlrichDiez I learn something new every day. Thank you. Now if we could only get an implementation of \the\unhbox...all would be well, LOL. – Steven B. Segletes Sep 19 '21 at 16:35
  • 2
    @UlrichDiez while the result is the same, \the\numexpr is (around 10%) faster than \number\numexpr (there is a difference however between \number\dimexpr and \the\dimexpr). – Skillmon Sep 20 '21 at 18:26
  • @StevenB.Segletes \numexpr on its own isn't expandable, it is more or less the same as using \count, just that it computes the value on the fly based on the input instead of fetching it from a register. – Skillmon Sep 20 '21 at 18:28
  • Thank you @Skillmon for adding content about \numexpr! – Steven B. Segletes Sep 20 '21 at 18:30
  • 1
    Ok, the 10% estimate was a little low. \the is more like 30% faster. – Skillmon Sep 20 '21 at 18:40
4

It's better to separate \price and \pricesymbol.

\documentclass{article}
\usepackage{xcolor}

\NewDocumentCommand{\pricesymbol}{}{\textcolor{black!60}{$}}

\ExplSyntaxOn

\NewDocumentCommand{\price}{m} { \prg_replicate:nn { #1 } { \pricesymbol } }

\ExplSyntaxOff

\begin{document}

\price{4} -- Fancy Expensive Steakhouse

\price{2} -- Pizza House (no pineapple)

\end{document}

enter image description here

You can even have fractional price symbols.

\documentclass{article}
\usepackage{xcolor}
\usepackage{trimclip}

\NewDocumentCommand{\pricesymbol}{}{\textcolor{black!60}{$}}

\ExplSyntaxOn

\NewDocumentCommand{\price}{m} { \prg_replicate:nn { \fp_eval:n { trunc(#1,0) } } { \pricesymbol } \fp_compare:nT { #1 - trunc(#1,0) > 0 } { \clipbox{0~0~{\fp_eval:n { 1 - #1 + trunc(#1,0) }\width}~0} { \pricesymbol } , } }

\ExplSyntaxOff

\begin{document}

\price{4} -- Fancy Expensive Steakhouse

\price{2} -- Pizza House (no pineapple)

\price{2.3} -- Pizza House (no pineapple)

\price{2.5} -- Pizza House (no pineapple)

\price{2.7} -- Pizza House (no pineapple)

\end{document}

enter image description here

egreg
  • 1,121,712
2

Tail-recursive, only traditional TeX:

\documentclass{article}

\newcommand\priceloop[1]{\if#1m$\expandafter\priceloop\fi} \newcommand\price[1]{\expandafter\priceloop\romannumeral\number\number#1 000\relax}

\begin{document}

\price{12}

\end{document}

Tail-recursive, eTeX-extensions:

\documentclass{article}

\newcommand\exchange[2]{#2#1}% \newcommand\price[1]{\ifnum#1>0 \exchange{$\expandafter\price\expandafter{\the\numexpr#1-1\relax}}\fi}

\begin{document}

\price{12}

\end{document}

Or use one of David Kastrup's \replicate-variants:

\documentclass{article}

\newcommand\gobble[1]{} \newcommand\xii[2]{\if#2m#1\expandafter\xii\else\expandafter\gobble\fi{#1}} \newcommand\xiii[2]{\xii{#2}#1\relax} \newcommand\replicate[1]{\expandafter\xiii\expandafter{\romannumeral\number\number#1 000}}%

\newcommand\price[1]{\replicate{#1}{$}}

\begin{document}

\price{12}

\end{document}

\documentclass{article}

\newcommand\recur[1]{\csname rn#1\recur}
\newcommand\rnm[1]{\endcsname{#1}#1}
\newcommand\rn[1]{}
\newcommand\replicate[1]{\csname rn\expandafter\recur\romannumeral\number\number#1 000\endcsname\endcsname}

\newcommand\price[1]{\replicate{#1}{\$}}

\begin{document}

\price{12}

\end{document}

The sequence \romannumeral\number\number#1 000 instead of just \romannumeral#1000 is for the following reason:

#1 is to be some TeX-⟨number⟩-quantity that is to be multiplied by 1000 by attaching digits 000 so that \romannumeral delivers the corresponding amount of character-tokens m.

#1 is not necessarily a sequence of digits.

It could be a \count-register as well where you need a space-token for separating the number denoting the \count-register from the three zeros to attach to the tokens forming the value held in that \count-register:

Assume you wish to denote the value held in \count-register 17:

\romannumeral\count17000 will not work out because TeX assumes an attempt of denoting the value held in \count-register 17000 and raises an error as there is no \count-register 17000.

\romannumeral\number\number\count17 000 does work out:

The second \number delivers the value that is held in \count-register 17 as digit-sequence. The space behind 17/before 000 is consumed as end-marker of the digit-sequence 17 denoting the \count-register.

The first \number is not needed here/does not change the fact that when the second \number is finished you already have a digit-sequence with three zeros appended which can be processed by \romannumeral.

But the TeX-⟨number⟩-quantity could be a \countdef-token or a \chardef-token or the like as well whereafter a space-token will not be removed by TeX.

Assume someone doing \chardef\sixtyfive=`\A.
\romannumeral\sixtyfive000 will not work out because due to TeX's rules for gathering TeX-⟨number⟩-quantities with \chardef-tokens 000 will not be considered part of the number to be processed by \romannumeral.

But \romannumeral\number\number\sixtyfive⟨space-token⟩000 will work out:

The second \number delivers 65 from the chardef-token \sixtyfive. This 65 is trailed by the ⟨space-token⟩ and the three zeros. The first \number finds the digit-sequence 65 trailed by a ⟨space-token⟩ and eliminates that ⟨space-token⟩. Now \romannumeral as number to convert can gather 65000 and deliver 65 characters m.

Ulrich Diez
  • 28,770
  • I have understood the first one. At first I did not understand how it could work without any -1. The \if is comparing a bunch of mmmmmm... and every cycle one is added and two are gobbled, not? Very nice. But why the two \number? If I take out one it seems to work the same. I can take out both if I write \romannumeral#1000 – Raoul Kessels Sep 20 '21 at 09:29
  • @RaoulKessels I added explanation for the two \number to my answer.||The gist of the first one is: Behind the token \priceloop you get characters m in an amount corresponding to \price's #1, trailed by \relax. The tail-recursive macro \priceloop gathers an argument, via \if compares its character-code to the character-code of m. Only if character-codes are equal both \$ is delivered and \priceloop calls itself again. So the trailing \relax, which as control-sequence in \if-comparisons is assumed to have a character-code which no character has, terminates the loop. – Ulrich Diez Sep 20 '21 at 20:21
  • @RaoulKessels The characters m in an amount corresponding to \price's #1 are delivered by \romannumeral - attaching 000 means multiplying by 1000. 1000 is m in roman notation. Multiples of 1000 are multiple m in roman notation... – Ulrich Diez Sep 20 '21 at 21:00
  • Thx for your very thorough explanation. That makes it clear – Raoul Kessels Sep 21 '21 at 05:49