1

This works:

\def\myfunc#1{\expandafter\myfuncdoit#1\relax}
\def\myfuncdoit#1:#2:#3\relax{ First: (#1), Second: (#2), Third (#3) }

\foreach \p in {x:foo:int,y:bar:float,z:baz:bool}{ (\myfunc{\p}) }

It produces

First: (x), Second: (foo), Third (int) First: (y), Second: (bar), Third (float) First: (z), Second: (baz), Third (bool)

However, I need to invoke this \foreach over a list of pairs, with a shared first string:

\foreach \p in {foo:int,bar:float,baz:bool}{ (\myfunc{abc:\p}) }

I expected this to produce

First: (abc), Second: (foo), Third (int) First: (abc), Second: (bar), Third (float) First: (abc), Second: (baz), Third (bool)

but it doesn't compile

Runaway argument?
float,baz:bool, \pgffor@stop , 
Paragraph ended before \myfuncdoit was complete.
<to be read again> 
                   \par

Why doesn't it work and how do I fix it? Thanks.

  • You need to expand the argument, otherwise \myfuncdoit sees abc:\p and doesn't see the second : delimiter. – Manuel Mar 9 '17 at 10:29
  • It does not work because \p is not expanded before it is passed to \myfuncdoit, only the first token of #1 is expanded (the a of abc). If the argument is fully expandable you could use \edef\reserved@a{#1}\expandafter\myfuncdoit\reserved@a\relax. But I cannot test this, because unfortunately you have not posted a MWE. – Schweinebacke Mar 9 '17 at 10:31
0

You have to make sure that at the point where the arguments of \myfuncdoit are scanned, all separators are in place. No expansion takes place at this moment.

You can do it like that.

\newcommand\myfuncx[2]{\myfuncdoit#2:#1\relax}

\foreach \p in {foo:int,bar:float,baz:bool}
  { (\expandafter\myfuncx\expandafter{\p}{abc}) }

enter image description here

\documentclass{article}
\usepackage{pgffor}
\newcommand\myfuncx[2]{\myfuncdoit#2:#1\relax}
\def\myfuncdoit#1:#2:#3\relax{ First: (#1), Second: (#2), Third (#3) }
\begin{document}
\foreach \p in {foo:int,bar:float,baz:bool}
      { (\expandafter\myfuncx\expandafter{\p}{abc}) }
\end{document}
  • I accepted this answer because it is more concise and requires no libraries, but it bothers me that {abc} comes after {\p} -- not that it matters except when I tried to adjust your solution so that {abc} is the first argument, it wouldn't compile. Am I just being thick or is there a reason you switched the order around? – spraff Mar 9 '17 at 15:41
  • 1
    @spraff The point is that I want to expand \p before expanding \myfuncx. \expandafter tells TeX to expand the second next token before expanding the next one. So \expandafter\xxx\expandafter{\yyy will expand first \yyy and only then \xxx followed by { followed by whatever resulted from the expansion of \yyy. If I switch the order resulting in something like \xxx{abc}{\yyy}, then I cannot reach \yyy with \expandafters anymore. – gernot Mar 9 '17 at 15:48
1

An easy extension of my previous answer:

\documentclass{article}
\usepackage{xparse}

\NewDocumentCommand{\myfuncthree}{>{\SplitArgument{2}{:}}m}{%
  \myfuncsplitthree#1%
}
\NewDocumentCommand{\myfuncsplitthree}{mmm}{%
  First: (#1), Second: (#2), Third: (#3).%
}

\ExplSyntaxOn
\NewDocumentCommand{\cycle}{ m +m }
 {
  \clist_map_inline:nn { #1 } { #2 }
 }
\ExplSyntaxOff

\begin{document}

\cycle{foo:int, bar:float, baz:bool}{\myfuncthree{abc:#1}\par}

\end{document}

enter image description here

If you instead call

\cycle{foo:int, bar:float, baz:bool}{\myfuncthree{#1:zzz}\par}

you get the expected result.

enter image description here

1

Here is a solution using \edef to expand the argument of \myfunc:

\documentclass{article}
\usepackage{pgffor}

\makeatletter
\def\myfunc#1{\edef\my@tmp{#1}\expandafter\myfuncdoit\my@tmp\relax}
\def\myfuncdoit#1:#2:#3\relax{ First: (#1), Second: (#2), Third (#3) }
\makeatother

\begin{document}
\foreach \p in {x:foo:int,y:bar:float,z:baz:bool}{ (\myfunc{\p})\par }
\foreach \p in {foo:int,bar:float,baz:bool}{ (\myfunc{abc:\p})\par }
\end{document}

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.