4

I'd like to do something along the lines of this question but is trying to do something much simpler. The following macro (I'd like to attribute it, but can't remember where I learned it)

\makeatletter
\def\myNorm#1{%
    \def\tmp{#1}
    \ifx\tmp\@empty%
        {\rho}
    \else%
        {\rho(#1)}
    \fi%
    }
\makeatother

puts parens around the second argument if it's not empty, e.g.,

$\myNorm{5}$ and $\myNorm{}$ return resp $\rho(5)$ and $\rho$.

The construction above is a bit of a mouthful, and hard to remember, but I use it a lot. So I'd like to automate the macro-creating process. Specifically, what I'd like to do is create a macro-creating macro that would look something like

\MacroTemplate#1#2

so that the first argument would be the name of a new macro, e.g., myNorm, and the second would be, say, \rho, such that

\MacroTemplate{myNorm}{\rho}

would construct the macro that I defined explicitly above. Then I could proceed to write $\myNorm{\rho}$ and $\myNorm{\rho}{5}$ which would return as above. But I could also then define

\MacroTemplate{yourNorm}{\nu}

and and write $\yourNorm{\nu}{5}$ and $\yourNorm{\nu}{}$ which would return $\beta(5)$ and $\beta$ respectively

without having to remember the complicated construction every time I want something similar. thanks very much for any suggestions.

3
  • A tip: You can use backticks ` to mark your inline code as I did in my edit.
    – ebosi
    Commented Jan 12, 2017 at 10:00
  • 1
    Why do you still have \rho and \nu as arguments of the final macros? Aren't the values given to \MacroTemplate already hard-coded in the definition of the final macros?
    – gernot
    Commented Jan 12, 2017 at 10:12
  • 1
    note you have % on lines where they do nothing (after \@empty, \else and \fi) but are missing % where they would do something (after the }) Commented Jan 12, 2017 at 10:30

3 Answers 3

2

Your definition is

\makeatletter
\def\myNorm#1{%
    \def\tmp{#1}
    \ifx\tmp\@empty%
        {\rho}
    \else%
        {\rho(#1)}
    \fi%
    }
\makeatother

but it should be

\makeatletter
\def\myNorm#1{%
    \def\tmp{#1}%
    \ifx\tmp\@empty
        \rho
    \else
        \rho(#1)%
    \fi
}
\makeatother

You don't get spurious spaces because you use the macro in math mode, where spaces are ignored anyway. There's no need for braces around the “true” and “false” texts: actually the braces could be dangerous in different situations.

I omitted the % after control sequence ending lines, because the space (or end-of-line) after them is ignored anyway when TeX reads the input file.

The first thing to straighten up is the check for the empty argument, which is better done with higher level macros, for example from etoolbox, which provides \ifblank:

\usepackage{etoolbox}

\newcommand{\myNorm}[1]{\rho\ifblank{#1}{}{(#1)}}

Now defining your template macro is very easy:

\newcommand\MacroTemplate[2]{%
  \newcommand{#1}[1]{#2\ifblank{##1}{}{(##1)}}%
}

The parameter in the command to be defined by the template macro becomes ##1.

So your \myNorm macro can be defined by

\MacroTemplate{\myNorm}{\rho}

It would be conceptually better to use a syntax such as

\myNorm
\myNorm[5]

with an optional argument (but this requires changing all your documents):

\newcommand{\MacroTemplate}[2]{%
  \newcommand{#1}[1][]{#2\ifblank{##1}{}{(##1)}}%
}

With xparse, the second way is simple and doesn't require etoolbox:

\usepackage{xparse}
\NewDocumentCommand{\MacroTemplate}{mm}{%
  \NewDocumentCommand{#1}{o}{#2\IfValueT{##1}{(##1)}}%
}
4

I think you would like somethink like:

\documentclass{article}

\usepackage{scrbase}% provides \IfArgIsEmpty

\newcommand*{\MacroTemplate}[2]{%
  \newcommand*#1[1]{#2\IfArgIsEmpty{##1}{}{(##1)}}%
}

\MacroTemplate\myNorm\rho
\MacroTemplate\yourNorm\nu

\begin{document}

$\myNorm{5}$ vs. $\myNorm{}$ and $\yourNorm{5}$ vs. $\yourNorm{}$

\end{document}

or

\documentclass{article}

\usepackage{scrbase}% provides \IfArgIsEmpty

\newcommand*{\CsTemplate}[2]{%
  \expandafter\newcommand\expandafter*\csname #1\endcsname[1]{#2\IfArgIsEmpty{##1}{}{(##1)}}%
}

\CsTemplate{myNorm}{\rho}
\CsTemplate{yourNorm}{\nu}

\begin{document}

$\myNorm{5}$ vs. $\myNorm{}$ and $\yourNorm{5}$ vs. $\yourNorm{}$

\end{document}

Note, that \IfArgIsEmpty is an emproved version of your emptiness test.

Explanation: #1 and #2 are the arguments of the outer \newcommand, while ##1 ist the argument of the inner \newcommand. You have to double # in each nesting.

You could also use an optional argument instead of an empty mandatory argument for the newly defined commands:

\documentclass{article}

\usepackage{scrbase}% provides \IfArgIsEmpty

\newcommand*{\CsTemplate}[2]{%
  \expandafter\newcommand\expandafter*\csname #1\endcsname[1][]{#2\IfArgIsEmpty{##1}{}{(##1)}}%
}
\newcommand*{\MacroTemplate}[2]{%
  \newcommand*#1[1][]{#2\IfArgIsEmpty{##1}{}{(##1)}}%
}

\CsTemplate{myNorm}{\rho}
\MacroTemplate\yourNorm{\nu}

\begin{document}

$\myNorm[5]$ vs. $\myNorm$ and $\yourNorm[5]$ vs. $\yourNorm$

\end{document}
1
  • Wow, a smorgasbord of alternatives. I picked @egreg's solution because it was the simplest and because I'm used to defining macros in the form of \rho{5}, etc, and his method is consistent with this. It's really helpful to know how to do it in other ways, though.
    – Leo Simon
    Commented Jan 12, 2017 at 21:38
3

Here is a version with xparse, using an optional argument (which is the better way to indicate an omittable argument, in my point of view).

There's a way of using g instead of o specifier to use it with omittable {} pair.

Anyway, an \expandafter...\csname ...\endcsname instruction is needed to construct the macro name.

\documentclass{article}

\usepackage{xparse}
\makeatletter
\newcommand\MacroGenerator[2]{%
\expandafter\NewDocumentCommand\csname #1\endcsname{o}{%
    \IfValueTF{##1}
    {%
      #2(##1)%
    }{%
      #2%
    }
  }
}

\makeatother
\begin{document}
\MacroGenerator{mynorm}{\rho}
$
\mynorm$ and $\mynorm[5]$

\end{document}

You must log in to answer this question.

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