5

I have to execute a macro a number of times, and would like to provide a list of the parameters to be executed. So, instead of

\FormatLinks*[Main Search Site]{Google}{http://www.google.com}
\FormatLinks[]{Google}{http://www.google.com}
\FormatLinks{Yahoo}{http://www.yahoo.com}

I would prefer to simply set a list:

\DefineMyFormatLinkParameters{%
    *[Main Search Site]{Google}{http://www.google.com},
    []{Google}{http://www.google.com},
    {Yahoo}{http://www.yahoo.com},
}

and then execute the \FormatLinks macro \foreach each element of this list. I think this is probably some expansion related issue, but I was not able to get it to work. Currently, the output is:

enter image description here

Code:

\documentclass{article}
\usepackage{url}
\usepackage{pgffor}
\usepackage{xparse}
\usepackage{xstring}
\usepackage[colorlinks=true]{hyperref}

\NewDocumentCommand{\FormatLinks}{%
    s%   #1 =* not used yet
    O{}% #2 = optional title
    m%   #3 = Mandatory title
    m%   #4 = URL Link
}{%
    \par
    \IfStrEq{#2}{}{%
        \hspace*{1.0cm}\href{#4}{#3}%
    }{%
        \hspace*{1.0cm}\href{#4}{#3~(#2)}%
    }%
}%

\newcommand*{\MyFormatLinkParameters}{}% Initialize
\newcommand*{\DefineMyFormatLinkParameters}[1]{%
    \edef\MyFormatLinkParameters{#1}%
}%

\begin{document}
\FormatLinks*[Main Search Site]{Google}{http://www.google.com}
\FormatLinks[]{Google}{http://www.google.com}
\FormatLinks{Yahoo}{http://www.yahoo.com}

% Would prefer to define a list, and later execute the list:
\DefineMyFormatLinkParameters{%
    *[Main Search Site]{Google}{http://www.google.com},
    []{Google}{http://www.google.com},
    {Yahoo}{http://www.yahoo.com},
}

\bigskip% 
Following should produce same results as above:\medskip\par
\foreach \x in \MyFormatLinkParameters {%
    \typeout{DEBUG: "\x"}
    \FormatLinks{\x}
}

\end{document}
4

Two things are wrong here:

  1. Expansion of \x should be done and not passed as a single argument to \FormatLinks.

    So, instead of

    \FormatLinks{\x}
    

    use

    \expandafter\FormatLinks\x
    

    since this will leave an expanded \x in the input stream for \FormatLinks to gobble up.

  2. You have an empty item in the list (the last item);

    The empty list item implies that when you're passing \x to \FormatLinks it is expecting at least two arguments yet it receives none. So it gobbles whatever comes along and obviously does the wrong thing.

enter image description here

\documentclass{article}
\usepackage{url}
\usepackage{pgffor}
\usepackage{xparse}
\usepackage{xstring}
\usepackage[colorlinks=true]{hyperref}

\NewDocumentCommand{\FormatLinks}{%
    s%   #1 =* not used yet
    O{}% #2 = optional title
    m%   #3 = Mandatory title
    m%   #4 = URL Link
}{%
    \par
    \IfStrEq{#2}{}{%
        \hspace*{1.0cm}\href{#4}{#3}%
    }{%
        \hspace*{1.0cm}\href{#4}{#3~(#2)}%
    }%
}%

\newcommand*{\MyFormatLinkParameters}{}% Initialize
\newcommand*{\DefineMyFormatLinkParameters}[1]{%
    \edef\MyFormatLinkParameters{#1}%
}%

\begin{document}
\FormatLinks*[Main Search Site]{Google}{http://www.google.com}
\FormatLinks[]{Google}{http://www.google.com}
\FormatLinks{Yahoo}{http://www.yahoo.com}

% Would prefer to define a list, and later execute the list:
\DefineMyFormatLinkParameters{%
    *[Main Search Site]{Google}{http://www.google.com},
    []{Google}{http://www.google.com},
    {Yahoo}{http://www.yahoo.com}
}

\bigskip% 
Following should produce same results as above:\medskip\par
\foreach \x in \MyFormatLinkParameters {%
    \typeout{DEBUG: "\x"}
    \expandafter\FormatLinks\x
}

\end{document}
  • Thanks. I had tried \expandafter\FormatLinks{\x}, but did not try \expandafter\FormatLinks\x. – Peter Grill Apr 15 '15 at 23:22
4

Why the mysterious \IfStr{#2}{} when xparse already provides the infrastructure?

\NewDocumentCommand{\FormatLinks}{%
    s%   #1 =* not used yet
    o%   #2 = optional title
    m%   #3 = Mandatory title
    m%   #4 = URL Link
}{%
  \par
  \hspace*{1.0cm}\href{#4}{#3\IfValueT{#2}{~#2}}%
}

The problem is that you don't want to do \FormatLinks{\x} but want to expand \x before \FormatLinks sees it; however the braces should not be there. So \expandafter\FormatLinks\x would do.

Better, use a clist and a mapping function.

\documentclass{article}
\usepackage{url}
\usepackage{xparse}
\usepackage[colorlinks=true]{hyperref}

\NewDocumentCommand{\FormatLinks}{%
    s%   #1 =* not used yet
    o%   #2 = optional title
    m%   #3 = Mandatory title
    m%   #4 = URL Link
}{%
  \par
  \hspace*{1.0cm}\href{#4}{#3\IfValueT{#2}{~#2}}%
}

\ExplSyntaxOn
\NewDocumentCommand{\FormatLinksList}{}
 {
  \clist_map_inline:Nn \g_grill_linkparameters_clist { \FormatLinks ##1 }
 }
\NewDocumentCommand{\DefineMyFormatLinkParameters}{m}
 {
  \clist_gset:Nn \g_grill_linkparameters_clist { #1 }
 }
\ExplSyntaxOff

\begin{document}
\FormatLinks*[Main Search Site]{Google}{http://www.google.com}
\FormatLinks[]{Google}{http://www.google.com}
\FormatLinks{Yahoo}{http://www.yahoo.com}

% Would prefer to define a list, and later execute the list:
\DefineMyFormatLinkParameters{
    *[Main Search Site]{Google}{http://www.google.com},
    []{Google}{http://www.google.com},
    {Yahoo}{http://www.yahoo.com},
}

\bigskip

Following should produce same results as above:\medskip\par

\FormatLinksList

\end{document}

enter image description here

  • I didn't know that \IfValueT would properly handle the case where the user specified []. That is really helpful to know. – Peter Grill Apr 15 '15 at 23:28
  • @PeterGrill It returns true only if the user specifies [...]. There are also \IfValueF and \IfValueTF; \IfValueT is equivalent to \IfNoValueF, but in this case it's semantically preferable the “positive” one. – egreg Apr 15 '15 at 23:31

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.