5

I want to create a macro \foo which creates another macro which a custom name set just before the execution of \foo via a macro \defcommand{myCustomName} and saves/adds something to it just as the following pseudo-latex-code suggests. Is it possible to translate this to a working LaTeX example?

%\defcommand needs to be defined
%addtocommand needs to be defined
\newcommand{\foo}[1]{
%If the command \???CustomString??? doesn't exist, create it and then add the content of #1 to it.
\addtocommand{???CustomString???}{#1}
}

Some text

\defcommand{customA}
\foo{A1} \foo{A2} \foo{A3} %should create a command named `customA` (instead of ???CustomString???) which I can use in the whole document. 

\customA %this schould print A1 A2 A3    

some text...

\defcommand{customB}
\foo{B1},\foo{B2} %now \foo should create another command named `customB`...

\customB %should print A1 A2 A3

\customA %should print B1 B2

Note that I don't want to use an argument of \foo to do this. I.e. I don't want to write \foo{Ax}{\customA}.

2
  • A syntax such as \foo{A}{1} would be preferable, unless the prefix is always one letter only.
    – egreg
    Commented Oct 1, 2012 at 14:26
  • In my use cases I would have to write lots of foo's wit the same second argument. For example \foo{A1}{\mymacroA} \foo{A2}{\mymacroA} \foo{A3}{\mymacroA}... so in my case it would much easier to write something like \defcommand{\mymacroA} and then just \foo{A1} \foo{A2} \foo{A3}...
    – student
    Commented Oct 1, 2012 at 14:32

1 Answer 1

8

If the prefix is always one letter, then you can do it:

\makeatletter
\newcommand{\defcommand}[1]{\@namedef{custom#1}{}}
\newcommand{\foo}[1]{\@foo#1\@nil}

\def\@foo#1#2\@nil{%
  \expandafter\ifx\csname custom#1\endcsname\@empty
    \expandafter\g@addto@macro\csname custom#1\endcsname{#1#2}%
  \else
    \expandafter\g@addto@macro\csname custom#1\endcsname{ #1#2}%
  \fi}
\makeatother

\defcommand{A}
\foo{A1}\foo{A2}\foo{A355}

\defcommand{B}
\foo{B123} \foo{Ba} \foo{B3}

\show\customA
\show\customB

This will print on the terminal

> \customA=macro:
->A1 A2 A355.
l.19 \show\customA

? 
> \customB=macro:
->B123 Ba B3.
l.20 \show\customB

Of course this wouldn't work if one wants

\defcommand{ABC}

and \foo{ABC1}, because it wouldn't be possible to know what's the prefix.

If the prefix may consist of more than one letter, I suggest a different syntax:

\makeatletter
\newcommand{\defcommand}[1]{\@namedef{custom#1}{}}
\newcommand{\foo}[2]{%
  \expandafter\ifx\csname custom#1\endcsname\@empty
    \expandafter\g@addto@macro\csname custom#1\endcsname{#1#2}%
  \else
    \expandafter\g@addto@macro\csname custom#1\endcsname{ #1#2}%
  \fi}
\makeatother

\defcommand{A}
\foo{A}{1}\foo{A}{2}\foo{A}{355}

\defcommand{ABC}
\foo{ABC}{123} \foo{ABC}{a} \foo{ABC}{3}

\show\customA
\show\customABC

This will print on the terminal

> \customA=macro:
->A1 A2 A355.
l.17 \show\customA

? 
> \customABC=macro:
->ABC123 ABCa ABC3.
l.18 \show\customABC

If the intent is to establish a macro to which subsequent calls should add, then it's a different matter.

\makeatletter
\newcommand{\defcommand}[1]{%
  \gdef#1{}%
  \gdef\foo@current@command{#1}%
}
\newcommand{\foo}[1]{%
  \expandafter\ifx\foo@current@command\@empty
    \expandafter\g@addto@macro\foo@current@command{#1}%
  \else
    \expandafter\g@addto@macro\foo@current@command{ #1}%
  \fi
}
\makeatother

\defcommand{\macroA}
\foo{x} \foo{y} \foo{z}

\defcommand{\macroB}
\foo{Here}\foo{is}\foo{a}\foo{new}\foo{one}

\show\macroA
\show\macroB

This will print on the terminal

> \macroA=macro:
->x y z.
l.21 \show\macroA

? 
> \macroB=macro:
->Here is a new one.
l.22 \show\macroB

The trick is to define \foo always in terms of the same wrapper command, and to add to \defcommand a part that defines the wrapper command as the current macro that's initialized.

How does this last set of macros work? Let's see it in slow motion.

  1. Here's \defcommand:

    \newcommand{\defcommand}[1]{%
      \gdef#1{}%
      \gdef\foo@current@command{#1}%
    }
    

    If we say \defcommand{\mymacro} the effect will be to initialize \mymacro to have empty expansion. Then the (global) definition will make \foo@current@command expand to \mymacro (not to the contents of \mymacro, at least at the first expansion).

  2. Here's \foo

    \newcommand{\foo}[1]{%
      \expandafter\ifx\foo@current@command\@empty
        \expandafter\g@addto@macro\foo@current@command{#1}%
      \else
        \expandafter\g@addto@macro\foo@current@command{ #1}%
      \fi
    }
    

    This has some complications, so let's look at a simplified version:

    \newcommand{\foo}[1]{%
      \expandafter\g@addto@macro\foo@current@command{#1}%
    }
    

    One has to know that \g@addto@macro adds its second argument to the (parameterless) macro given as first argument. So, assume we have declared \defcommand{\mymacro} and then we do \foo{x}: this becomes

    \expandafter\g@addto@macro\foo@current@command{x}
    

    and now \expandafter comes into action, changing \foo@current@command to its first level expansion, which gives \mymacro, so now we have

    \g@addto@macro\mymacro{x}
    

    which does the desired thing.

  3. The more complex definition is used to ensure that no space is added in front of the first item we add:

    \newcommand{\foo}[1]{%
      \expandafter\ifx\foo@current@command\@empty
        \expandafter\g@addto@macro\foo@current@command{#1}%
      \else
        \expandafter\g@addto@macro\foo@current@command{ #1}%
      \fi
    }
    

    Let's see assuming that now \defcommand{\bar} has just been given, so that now \foo@current@command expands to \bar, whose expansion is empty. As soon as we say \foo{abc}\foo{def}, TeX will transform it into

    \expandafter\ifx\foo@current@command\@empty
      \expandafter\g@addto@macro\foo@current@command{#1}%
    \else
      \expandafter\g@addto@macro\foo@current@command{ #1}%
    \fi
    \foo{def}
    

    The first \expandafter changes \foo@current@command into \bar:

    \ifx\bar\@empty
      \expandafter\g@addto@macro\foo@current@command{abc}%
    \else
      \expandafter\g@addto@macro\foo@current@command{ abc}%
    \fi
    \foo{def}
    

    Now \bar has empty expansion, so the "true" branch is followed:

    \expandafter\g@addto@macro\foo@current@command{abc}%
    \foo{def}
    

    and, again,

     \g@addto@macro\bar{abc}\foo{def}
    

    so that, now, the expansion of \bar will be abc and TeX will examine \foo{def}. This will do exactly the same as before, but following the "false" branch, because \bar is no longer empty. So we'll have

    \expandafter\g@addto@macro\foo@current@command{ def}
    

    that becomes

    \g@addto@macro\bar{ def}
    

    and the final result will be the same as if we had said

    \def\bar{abc def}
    

    to begin with.

5
  • I just realized that my question was not clear enough... The first character in the argument of \foo doesn't have to match the (last character) of the custom macro. I.e. \defcommand{A} \foo{one}\foo{two}\foo{three} should add one two three to the macro \A (or \customA)
    – student
    Commented Oct 1, 2012 at 17:35
  • @student Maybe the third version is what you need.
    – egreg
    Commented Oct 1, 2012 at 17:59
  • ...but I don't really understand why this works. With my very limited TeX knowledge I think that: When I call \defcommand{\macroA}, TeX creates an empty macro named \macroA and then puts this empty content to another macro called \foo@current@command. When then \foo{x} is called, \foo@current@command will contain x. When I finaly call \macroA I would expect that it prints nothing. I guess that \gdef\foo@current@command\macroA somehow ensures that \macroA contains everything of \foo@current@command since the last call of \defcommand but I don't see why.
    – student
    Commented Oct 1, 2012 at 20:03
  • Perhaps this is because my understanding of the \def command is to limited... Would be nice if you could add some more explanations that I can grasp why it works.
    – student
    Commented Oct 1, 2012 at 20:04
  • @student I've added a longish explanation.
    – egreg
    Commented Oct 1, 2012 at 20:24

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .