1

I have a macro which uses inner macros to process pairs of arguments. The essential part can be broken down to

\mymacro{\inside{A}{1};\inside{B}{2},\inside{C}{3}}.

Originally I had all pairs processed and then printed, separated by commas, using a sequence to get an output like "1A, 2B, 3C", "1A, 2B" or "1A", for three, two and one arguments, respectively.

Now the requirements have changed and I need the output to be: "1,A 2,B 3.C", "1,A 2.B" or "1.A".

I figured, this could be done with sequences and came up with this solution:

\documentclass{article}
\usepackage{xparse,expl3} % expl3 loaded explicitly - not necessary because it is already loaded by xparse
\ExplSyntaxOn

\tl_new:N \l__map_current_tl
\tl_new:N \l__map_tl
\tl_new:N \l__inside_tl

\seq_new:N \l__my_seq
\seq_new:N \l__second_seq

\NewDocumentCommand \mymacro {>{\SplitList{;}} m}
{
    \mymacro_process_func:n {#1}
    \mymacro_output_func:n
    \\
}

\NewDocumentCommand \inside {m m}
{
    \inside_process_func:nn {#1} {#2}
}

\cs_new_protected:Npn \inside_process_func:nn #1 #2
{
    \tl_if_empty:NTF \l__inside_tl
    { \tl_put_right:Nn \l__inside_tl {#2 !! #1} }
    { \tl_put_right:Nn \l__inside_tl {~ #2 !! #1} }
}

\cs_new_protected:Npn \mymacro_process_func:n #1
{
    % make sure token lists and sequences are empty
    \tl_clear:N \l__map_current_tl
    \tl_clear:N \l__map_tl
    \tl_clear:N \l__inside_tl

    \seq_clear:N \l__my_seq
    \seq_clear:N \l__second_seq

    \tl_map_function:nN {#1} \mymacro_map_func:n
    \tl_show:N {\l__map_tl}
    \seq_set_split:NnV \l__my_seq {;} \l__map_tl
%   \seq_show:N \l__my_seq
    \seq_use:Nnnn \l__my_seq {} {} {}
}

\cs_new_protected:Npn \mymacro_map_func:n #1
{
    \tl_set:Nn \l__map_current_tl {#1}
    \tl_if_empty:NTF \l__map_tl
    { \tl_set_eq:NN \l__map_tl \l__map_current_tl }
    {
        \tl_put_right:Nn \l__map_tl {;}
        \tl_put_right:NV \l__map_tl \l__map_current_tl
    }
    \tl_set:Nn \l__map_current_tl {#1}
}

\cs_new_protected:Npn \mymacro_output_func:n 
{
    \seq_set_split:NnV \l__second_seq {!!} \l__inside_tl
%   \seq_show:N \l__second_seq
    \seq_use:Nnnn \l__second_seq {.} {,} {.}
}

\ExplSyntaxOff

\begin{document}

Achieved:\\
\mymacro{\inside{A}{1}}
\mymacro{\inside{A}{1};\inside{B}{2}}
\mymacro{\inside{A}{1};\inside{B}{2};\inside{C}{3}}

Desired:\\
1.A \\ 1,A 2.B \\ 1,A 2,B 3.C

\end{document}

As you can see, the code does what it should:

Sequences Output

However, I fear it is not very efficient and it looks very dubious to me. For example, I used \l__my_seq in \mymacro_process_func:n with spaces in a previous version:

\seq_set_split:NnV \l__my_seq {;} \l__map_tl
\seq_use:Nnnn \l__my_seq {~} {~} {~}

but the spaces did not appear in the output. (In fact, replacing all ~ by ? prints none, one or two question marks before the output, respectively.)

  • Is there a better (shorter) way to without the need to change the use of \mymacro and \inside? (I can't change all the macros in my document)
  • Is there, in general, a better approach to this problem?

By the way: I am not sure if my solution (naming, etc.) complies with all rules and conventions, so any comment on this is appreciated as well.

1 Answer 1

1

If I understand the requirement it seems you don't need to loop over the input you can simply execute the list, typesetting each item as you go.

enter image description here

\documentclass{article}

\def\mymacro#1{#1\relax}
\def\inside#1#2#3{\ifx\relax#3#2.#1\else#2,#1 \fi}

\begin{document}


Achieved:\\
\mymacro{\inside{A}{1}}\\
\mymacro{\inside{A}{1};\inside{B}{2}}\\
\mymacro{\inside{A}{1};\inside{B}{2};\inside{C}{3}}

Desired:\\
1.A \\ 1,A 2.B \\ 1,A 2,B 3.C


\end{document}
3
  • Wow, this is much shorter. Will it also work if I had to further process the numbers? I did not mention it in the minimal example to keep it minimal but some of the numbers could be ranges which I inserted using a SplitList in the \inside macro as well, using this in an \SIrange.
    – Echo
    May 17, 2020 at 11:31
  • @Echo if the processing of each \inside is independent of others then the same basic technique will work, even if you code the more complicated \inside logic using expl3 May 17, 2020 at 11:55
  • Due to my lack of experience I don't see how, but I will take a closer look. I will accept your answer - as it does all I asked for. Thanks for sharing your knowledge.
    – Echo
    May 17, 2020 at 12:09

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.