1

I'll explain what I mean with an example.

\documentclass{article}
\usepackage{arrayjobx}
\newcounter{foo}
\setcounter{foo}{0}
\renewcommand{\bar}{\addtocounter{foo}{1}Some text}
\newarray\baz
% -------------------
\begin{document}
\baz(1)={\bar}
\arabic{foo} % foo should be 1

\baz(1), \arabic{foo} % foo should still be 1

\baz(1), \arabic{foo} % foo should once again be 1
\end{document}

When I assign the command \bar to the array, it isn't expanded and it gets assigned to the array untouched, which means that the counter foo stays at zero until I call \baz(1).

Is it possible to make it so that \bar gets expanded right away when assigned to the array \baz, so that the counter is increased only once, and the value of \baz(1) is simply equal to Some text?

8
  • So \baz(1) shouldn't contain any \addtocounter anymore? That is not possible with simple expansion, one would have to parse the first token of \bar to see whether it is an \addtocounter, if so execute that \addtocounter with its arguments, removing it from \bar and assigning \baz(1) with the remainder.
    – Skillmon
    Jun 23 '19 at 19:32
  • That is the idea. In this mwe used the counter and \addtocounter as a way to see whether or not the argument was expanded or not, but in my actual problem (which is too long to post here) there are no counters, so I'm looking for a general solution. Basically the value of \baz(1) should be just the text part of whatever is contained in the command \bar.
    – noibe
    Jun 23 '19 at 19:47
  • That seems pretty hard to do reliably, one would have to implement TeX (or a big subset of it), to really parse the contents of the macro and remove anything that isn't producing text. I'd pass from this. What do you need this for? Can you explain your use case, perhaps you're tackling the problem at hand in the wrong way and there is a better alternative.
    – Skillmon
    Jun 23 '19 at 19:52
  • By the way, in the meantime I had implemented what my first comment suggested :( So I have a macro that you can use to remove \addtocounter from your macros.
    – Skillmon
    Jun 23 '19 at 19:53
  • Well to be fair in the post I didn't specify the degree of generality that I needed the solution to have, so post the answer and I'll upvote it.
    – noibe
    Jun 23 '19 at 20:02
3

The following adds two macros \addtoarraywithcounter and \addtoarrayasbox. The former can handle an unbraced \addtocounter in the argument (and expands the argument once if it is only one token, e.g. a single macro, before processing it).

The latter is pretty short (8 lines of code). It can handle almost arbitrary macro contents and only includes the typeset result to the array. It does so by typesetting the argument inside a box and storing the box inside the array. Code not acting on a global scope wouldn't have any effect outside of the typeset box (in the example below I've added a \def\baz{hihi} to the definition of \bar to show that this won't change the definition of \baz). LaTeX's \addtocounter however sets the counter globally, so it affects the code outside the third argument of \addtoarrayasbox.

\documentclass{article}
\usepackage{arrayjobx}
\newcounter{foo}
\setcounter{foo}{0}
\renewcommand{\bar}{\addtocounter{foo}{1}Some text\def\baz{hihi}}
\newarray\baz
% -------------------

\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand \addtoarraywithcounter { m m +m }
  {
    % if only one token is handed in and that is not a space
    \bool_if:nTF
      { \tl_if_single_token_p:n { #3 } && \tl_if_head_is_N_type_p:n { #3 } }
      {
        % expand that token, it might be a macro
        \noibe_array_with_counter:nno { #1 } { #2 } { #3 }
      }
      {
        % seems to be a more than a single token
        \noibe_array_with_counter:nnn { #1 } { #2 } { #3 }
      }
  }
\cs_new_protected:Npn \noibe_array_with_counter:nnn #1 #2 #3
  {
    \tl_if_in:nnTF { #3 } { \addtocounter }
      {
        \noibe_handle_addtocounter:nnw { #1 } { #2 } \q_nil #3 \q_stop
      }
      {
        \noibe_add_to_array:nnn { #1 } { #2 } { #3 }
      }
  }
\cs_new_protected:Npn
  \noibe_handle_addtocounter:nnw #1 #2 #3 \addtocounter #4 #5
  {
    \addtocounter { #4 } { #5 }
    \noibe_handle_addtocounter_auxi:nnow { #1 } { #2 } { \use_none:n #3 } \q_nil
  }
\cs_new_protected:Npn \noibe_handle_addtocounter_auxi:nnnw #1 #2 #3 #4 \q_stop
  {
    \noibe_handle_addtocounter_auxii:nnno
      { #1 } { #2 } { #3 } { \use_none:n #4 }
  }
\cs_generate_variant:Nn \noibe_handle_addtocounter_auxi:nnnw { nnow }
\cs_new_protected:Npn \noibe_handle_addtocounter_auxii:nnnn #1 #2 #3 #4
  {
    \noibe_add_to_array:nnn { #1 } { #2 } { #3 #4 }
  }
\cs_generate_variant:Nn \noibe_handle_addtocounter_auxii:nnnn { nnno }
\cs_new_protected:Npn \noibe_add_to_array:nnn #1 #2 #3
  {
    #1 ( #2 ) = { #3 }
  }
\cs_generate_variant:Nn \noibe_array_with_counter:nnn { nno }
\NewDocumentCommand \addtoarrayasbox { m m m }
  {
    \cs_if_exist:cF { l__noibe_array_ \cs_to_str:N #1 _content_ #2 _box }
      { \box_new:c { l__noibe_array_ cs_to_str:N _content_ #2 _box } }
    \hbox_set:cn { l__noibe_array_ cs_to_str:N _content_ #2 _box } { #3 }
    #1 ( #2 ) = 
      { \hbox_unpack:c { l__noibe_array_ cs_to_str:N _content_ #2 _box } }
  }
\ExplSyntaxOff


\begin{document}
\addtoarrayasbox\baz{1}{\bar}
\addtoarraywithcounter\baz{2}{{hihi}\addtocounter{foo}{1}{haha}}
%\baz(1)={\bar}
\arabic{foo} % foo should be 2

\baz(1), \baz(2), \arabic{foo} % foo should still be 2

\baz(1), \baz(2), \arabic{foo} % foo should once again be 2
\end{document}

enter image description here

1
  • Even in its simplicity, the macro \addtoarrayasbox works beautifully in my real world use case.
    – noibe
    Jun 23 '19 at 21:23

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.