6

I have a macro that uses \prg_replicate:nn to produce a number of spaces, then pass those spaces, after f-expanding \prg_replicate:nn, to another macro. The problem is that when I use \exp_args:Nf, it will use the \romannumeral 0 thingy which can consume a space. Guess what, this space is removed from my replicated spaces, so after the whole thing is completed I have one space less.

The issue is reproduced with this MWE:

\documentclass{article}
\usepackage{expl3}
\begin{document}
\ExplSyntaxOn
\cs_new:Npn \__printf_pre_output:n #1 { [ \tl_to_str:n {#1} ] }
\cs_set:Npn \__printf_tmp:w #1
  {
    \cs_new:Npn \__printf_format_final_print:n ##1
      {
        \exp_args:Nf \__printf_pre_output:n
          { \prg_replicate:nn {##1} {#1} }
      }
  }
\__printf_tmp:w { ~ }
\ttfamily
Wrong,~4~spaces:~\__printf_format_final_print:n {5}\par
Right,~5~spaces:~[\ \ \ \ \ ]\par
\ExplSyntaxOff
\end{document}

which produces:

enter image description here

I couldn't find a way to stop \romannumeral from eating one of my spaces besides the obvious "do the whole thing with an extra space to begin with".

For the time being I'm doing the replication with an extra space to feed to \romannumeral, but it seems a hackish way to do that. Is there another possibility?

  • Is 'use another expansion method' a valid answer? With \expanded coming (already in MiKTeX), I'd just use e-type in new code. – Joseph Wright Jan 10 at 11:23
  • @JosephWright Ooh, there's that. If it isn't possible with f-type, then I'd be happy with e :) – Phelype Oleinik Jan 10 at 11:25
  • how many steps requires \prg_replicate:nn to fully expand ? if only two then you only need to expand twice. – user4686 Jan 10 at 11:38
  • @jfbu two steps. – Skillmon Jan 10 at 11:39
9

I would simply use the new e-type approach here (i.e. using the \expanded primitive or equivalent):

\documentclass{article}
\usepackage{expl3}
\begin{document}
\ExplSyntaxOn
\cs_new:Npn \__printf_pre_output:n #1 { [ \tl_to_str:n {#1} ] }
\cs_set:Npn \__printf_tmp:w #1
  {
    \cs_new:Npn \__printf_format_final_print:n ##1
      {
        \exp_args:Ne \__printf_pre_output:n
          { \prg_replicate:nn {##1} {#1} }
      }
  }
\__printf_tmp:w { ~ }
\ttfamily
Wrong,~4~spaces:~\__printf_format_final_print:n {5}\par
Right,~5~spaces:~[\ \ \ \ \ ]\par
\ExplSyntaxOff
\end{document}

This will likely become the standard approach for such cases: f-type expansion is much less important now that we have e-type either directly using the \expanded primitive or (where necessary) emulated. Note that MiKTeX already has \expanded in pdfTeX and XeTeX, and that this will appear in TeX Live 2019. Also note that LuaTeX has had \expanded from day one. (Emulation is reliable but does have a performance impact: at present, it probably is best avoided for any code which needs to work in tight loops or similar.)

4

Like @jfbu mentioned, expanding twice is enough for \prg_replicate:nn. So using \exp_args:No twice on it suffices:

\documentclass{article}
\usepackage{expl3}
\begin{document}
\ExplSyntaxOn
\cs_new:Npn \__printf_pre_output:n #1 { [ \tl_to_str:n {#1} ] }
\cs_set:Npn \__printf_tmp:w #1
  {
    \cs_new:Npn \__printf_format_final_print:n ##1
      {
        \exp_args:NNo \exp_args:No \__printf_pre_output:n
          { \prg_replicate:nn {##1} {#1} }
      }
  }
\__printf_tmp:w { ~ }
\ttfamily
Wrong,~4~spaces:~\__printf_format_final_print:n {5}\par
Right,~5~spaces:~[\ \ \ \ \ ]\par
\ExplSyntaxOff
\end{document}
  • Ooh, I got so deep in counting spaces that I forgot counting expansions :) – Phelype Oleinik Jan 10 at 11:52
4

I can only second @JosephWright and @Skillmon answers but speaking of what I know, with xint you only need to expand once \romannumeral\xintreplicate{100}{ } to get 100 spaces. So if inside a macro

\def\x{\romannumeral\xintreplicate{100}{ }}

you only need to expand \x twice.

Or

\def\x{\xintreplicate{100}{ }}

then you can do \romannumeral\x.

Oh wait, I completely forgot: \romannumeral\xintreplicate basically IS (up to different user interface) \prg_replicate:nn, I COPIED its underlying implementation ;-).

  • You naughty boy ;) But they are not exactly the same. If I use \expandafter\__printf_pre_output:n\expandafter{\romannumeral\xintreplicate{##1}{#1}} it works correctly, but if I use \expandafter\__printf_pre_output:n\expandafter{\romannumeral\prg_replicate:nn{##1}{#1}} I get a Missing number error... Either way, I'm sticking to expl3 in this code, so I'll leave xint for another time :) – Phelype Oleinik Jan 10 at 12:14
  • @PhelypeOleinik yes only the underlying implementation is the same, not the user interface with \romannumeral. I will fix my confusing wording. (of course xint never had ambition to provide programming environment like expl3 does; if I had known about expl3 when I started coding xint in 2013 I might have used it... but obviously I was not good at that time finding TeX documentation...) – user4686 Jan 10 at 12:28
  • 1
    not exactly the same (from the docs: Using it with a negative x raises no error and does nothing. and from code comment in sourcexint.pdf The code in Joseph's post does abs(#1) replications when input #1 is negative and then activates an error triggering macro; This refers to tex.stackexchange.com/questions/16189/repeat-command-n-times and I don't know if posted code there is still faithfully the one from expl3) – user4686 Jan 10 at 12:37
  • You probably need \romannumeral-`0 instead of just \romannumeral. – Henri Menke Jan 14 at 21:51
  • @HenriMenke I wrote \xintreplicate to be used with \romannumeral\xintreplicate syntax. – user4686 Jan 14 at 23:02

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.