2

Can I use pgffor to loop through macros (control sequences)? e.g. \foreach \macro in {\lions,\tigers,\bears}

In case you haven't figured it out, I love modularization!

Let's say I have three commands to patch:

  1. \a1
  2. \a2
  3. \a3

Pseudo-Code

This code was an attempt to achieve what I want. For fun and to test my understanding, I used \csname to create macros with numbers in their names.

\documentclass{article}
\usepackage{tikz}
\usepackage{regexpatch}
\expandafter\newcommand\csname a1\endcsname[1]{}
\expandafter\newcommand\csname a2\endcsname[1]{}
\expandafter\newcommand\csname a3\endcsname[1]{}

\foreach \macro in {\csname a1\endcsname,\csname a2\endcsname,\csname a3\endcsname} {%
  \expandafter\xapptocmd{\macro}{\unexpanded{#1}, oh my!}{}{}% Apply patches to all macros in list
}%
\begin{document}
\csname a1\endcsname{Lions}

\csname a2\endcsname{tigers}

\csname a3\endcsname{and bears}
\end{document}

Expected Page Output

Lions, oh my!

tigers, oh my!

and bears, on my!

Alternative semi-working Example without \csname

\documentclass{article}
\usepackage{tikz}
\usepackage{regexpatch}
\newcommand\lions[1]{}
\newcommand\tigers[1]{}
\newcommand\bears[1]{}

%\foreach \macro in {\lions,\tigers,\bears} {% Loop up the following junk
  \xapptocmd{\lions}{\unexpanded{#1}, oh my!}{}{}% inefficient use of my time
  \xapptocmd{\tigers}{\unexpanded{#1}, oh my!}{}{}% inefficient use of my time
  \xapptocmd{\bears}{\unexpanded{#1}, oh my!}{}{}% inefficient use of my time
%}%
\begin{document}
\lions{Lions}

\tigers{tigers}

\bears{and bears}
\end{document}

Output

enter image description here

  • The usage of numbers in a command name is not really recommendable – user31729 Jul 11 '16 at 11:32
  • @ChristianHupfer Understood, but what about this? I find it quite useful to have dynamic generation of macros tug.org/tug2012/booklet/hendrickson/AmyTugProc.pdf – Jonathan Komar Jul 11 '16 at 11:33
  • Well, you have to use \csname ....\endcsname each time you want to use it – user31729 Jul 11 '16 at 11:37
  • If you want to use your \foreach loop, you have to use ##1 instead of #1 – user31729 Jul 11 '16 at 11:52
  • @ChristianHupfer Where'd your answer go? I was about to accept it! I think it would be quite beneficial for the community to offer as many alternatives as possible! – Jonathan Komar Jul 11 '16 at 13:13
2

You can use, apart from using expl3 loops (like \clist_map_inline:nn), the provided by etoolbox.

\documentclass{scrartcl}

\usepackage{etoolbox,regexpatch}

\csdef{a1}#1{}
\csdef{a2}#1{}
\csdef{a3}#1{}

\begin{document}

\renewcommand*\do[1]{\expandafter\xapptocmd\csname#1\endcsname{##1, oh my!}{}{\typeout{Misreable filure}}}
\docsvlist{a1,a2,a3}

\csuse{a1}{Lions}\par
\csuse{a2}{tigers}\par
\csuse{a3}{and bears}

\end{document}

I used \csdef and \csuse just to show them, you can use \expandafter\newcommand\csname .. \endcsname and \csname .. \endcsname, like always.

As egreg says, the always repeating problem with \foreach is the fact that everything is inside a group. I've never thought about it, but I don't know why isn't it defined something like \foreach \foo in { a, b } { <code> } to be \let\save\foo \def\foo{a} <code> \def\foo{b} <code> \let\foo\save, which would solve many things. In any case, if you want to define yourself a complete macro that will automate everything you can do, for example, this two options:

\usepackage{etoolbox}
\newcommand\doforeach[2]{\renewcommand\do[1]{#2}\docsvlist{#1}}

or

\usepackage{expl3}
\ExplSyntaxOn
\NewDocumentCommand \doforeach { +m +m } { \clist_map_inline:nn { #1 } { #2 } }
\ExplSyntaxOff

and then you can use easily

\doforeach{a1,a2,a3}{\expandafter\xapptocmd\csname#1\endcsname{##1, oh my!}{}{\typeout{Misreable filure}}}

and you don't have to type difficult things.

  • Thanks! That is a pretty cool alternative. I am not sure which answer to accept because they all make useful contributions :) – Jonathan Komar Jul 11 '16 at 12:47
  • 1
    Misreable filure indeed ;) – Jonathan Komar Jul 11 '16 at 13:06
  • 1
    Answering to your question to Christian, if you want \protected@xdef inside expl3 code you can use it like \use:c { protected@xdef }. – Manuel Jul 11 '16 at 13:20
2

The usual problem: \foreach does its job in a group, so the patch is nullified as soon as the group ends. However, you're trying to patch \macro, which expands to \csname a1\endcsname and so on, so the attempt is obviously unsuccessful.

Define \temp to be the same as a “double expansion” of \macro; patch it; globally redefine the \csname macro to the patched version.

\documentclass{article}
\usepackage{tikz}
\usepackage{regexpatch}
\expandafter\newcommand\csname a1\endcsname[1]{#1}
\expandafter\newcommand\csname a2\endcsname[1]{#1}
\expandafter\newcommand\csname a3\endcsname[1]{#1}

\foreach \macro in {\csname a1\endcsname,\csname a2\endcsname,\csname a3\endcsname}
  {%
   \expandafter\expandafter\expandafter\let\expandafter\expandafter\expandafter\temp\macro
   \xapptocmd\temp{, oh my!}{}{}%
   \global\expandafter\expandafter\expandafter\let\macro\temp
  }

\begin{document}
\csname a1\endcsname{Lions}

\csname a2\endcsname{tigers}

\csname a3\endcsname{and bears}
\end{document}

enter image description here

Here's how you can do if you want the argument specifier in the patch. Note the doubling of #, because we're in the argument to \foreach.

\documentclass{article}
\usepackage{tikz}
\usepackage{regexpatch}
\expandafter\newcommand\csname a1\endcsname[1]{}
\expandafter\newcommand\csname a2\endcsname[1]{}
\expandafter\newcommand\csname a3\endcsname[1]{}

\foreach \macro in {\csname a1\endcsname,\csname a2\endcsname,\csname a3\endcsname}
  {%
   \expandafter\expandafter\expandafter\let\expandafter\expandafter\expandafter\temp\macro
   \xapptocmd\temp{##1, oh my!}{}{}%
   \global\expandafter\expandafter\expandafter\let\macro\temp
  }

\begin{document}
\csname a1\endcsname{Lions}

\csname a2\endcsname{tigers}

\csname a3\endcsname{and bears}
\end{document}
  • Thanks for explaining things. By the way, #1was put intentionally in the patch. In a real-world situation, my patch would be e.g. \typeout{==> #1 (2.2:Subsection Title:filepath:[pageno]:lineno)} (custom modularized/hot-swappable logging package) – Jonathan Komar Jul 11 '16 at 12:44
  • 1
    @macmadness86 I added the version with #1 in the patch – egreg Jul 11 '16 at 13:22
1

I would rather use \seq list and expand it inside.

\documentclass{article}
\usepackage{tikz}
\usepackage{regexpatch}
\expandafter\newcommand\csname macro1\endcsname[1]{}
\expandafter\newcommand\csname macro2\endcsname[1]{}
\expandafter\newcommand\csname macro3\endcsname[1]{}



\begin{document}
Before patching:

\csname macro1\endcsname{Lions}
\csname macro2\endcsname{Tigers}
\csname macro3\endcsname{and Bears}


\ExplSyntaxOn
\seq_set_from_clist:Nn \l_tmpa_seq {macro1,macro2,macro3}
\seq_map_inline:Nn \l_tmpa_seq  {%
 \exp_after:wN \xapptocmd\cs:w #1\cs_end:{##1,~oh~my!}{\typeout{Success!}}{}
}
\ExplSyntaxOff

After patching:

\csname macro1\endcsname{Lions}

\csname macro2\endcsname{Tigers}

\csname macro3\endcsname{and Bears}


\end{document}

Other version, using the correct parameter ##1 and and a by-pass with \global\csletcs, since \foreach runs it's code a group only.

\documentclass{article}
\usepackage{tikz}
\usepackage{etoolbox}
\usepackage{regexpatch}
\expandafter\newcommand\csname a1\endcsname[1]{}
\expandafter\newcommand\csname a2\endcsname[1]{}
\expandafter\newcommand\csname a3\endcsname[1]{}

\begin{document}

\foreach \macro in {a1,a2,a3} {%
  \let\santaslittlehelper\macro
  \expandafter\xapptocmd\csname \santaslittlehelper\endcsname{##1, oh my!}{}{\typeout{Miserable failure}}% Apply patches to all macros in list
  \global\csletcs{\macro}{\santaslittlehelper}
}
\csname a1\endcsname{Lions}

\csname a2\endcsname{tigers}

\csname a3\endcsname{and bears}

\end{document}

enter image description here

  • Nice. I was hoping to avoid LaTeX3 syntax. Why did you switch \xappcmd to \xpatchcmd? – Jonathan Komar Jul 11 '16 at 11:39
  • 1
    @macmadness86: Call it a typo ;-) – user31729 Jul 11 '16 at 11:45
  • @macmadness86: Look on the expl3-less version (which is not true, since regexpatch is a expl3 package ;-) – user31729 Jul 11 '16 at 12:10
  • "\santaslittlehelper" haha. Do you know how to put \protected@xdef into the exp3 version? I am not sure where to put the \protected@xdef because of aux. – Jonathan Komar Jul 11 '16 at 12:51
  • @macmadness86: There's no .aux issue in your question – user31729 Jul 11 '16 at 13:00

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.