7

When I was writing my thesis I wrote the following two commands which I found useful (and still do). They are both ways of defining other commands.

\usepackage{ifthen}
\makeatletter  
\def\optional #1[#2]#3#4{\newcommand{#1}[#2][@rGUmentmiSSing]{\ifthenelse{\equal{##1}{@rGUmentmiSSing}}{#4}{#3}}}  
\newcommand{\starredcommand}[4][*]{\newcommand{#2}{\@ifnextchar#1{\expandafter #4\@gobble}{#3}}}  
\makeatother

They aren't easy to read. The first one allows me to define a command with an optional argument, where the behaviour of the command is very different if the optional argument is there or not.

The second allows me to define a command with a modifier which I usually use if I have two versions of the same object but one defined on a larger space. For example I have

\optional{\foo}[1]{F(#1)}{F} 
\starredcommand{\barr}{\mathcal B}{\mathscr B}

So I can write

\begin{align}
    \foo &= \foo[x] \\ 
    \barr &\neq \barr*
\end{align}

Which would give me the same effect as

\begin{align}
    F &= F(x) \\ 
    \mathcal B &\neq \mathscr B
\end{align}

I like these commands but they don't play nicely together. For example if I write \foo[\bar] or \foo[\bar*] I get over 100 errors, which is bad. It's not to do with the \@gobble command eating the bracket. \foo[\bar* 123456789] Gives me the same thing. And it's not to do with the square brackets. If I define

\newcommand{\fine}[1][y]{X^{#1}}

then \fine[\bar] works exactly as it's supposed to.

I have no idea what's going wrong. It would be great if someone could help.

Here's a follow up question if that one's too easy. At the moment there's no way of including arguments in my \starredcommand If I wanted to give \barr an argument I'd normally do something like

\makeatletter
\newcommand{\@barr}[1]{\mathcal B^{#1}}
\newcommand{\@barrstar}{\mathscr B^{#1}}
\starredcommand{\barr}{\@barr}{\@barrstar}
\makeatother

Which works fine (even with optional arguments or my \optional command) but it sort of defeats the point of defining the \starredcommand in the first place. I'd love a version of starred command that could work with arguments but I can't think of a way of doing it.

Tim
  • 173
  • 5
    why don't you have a look at the xparse package, can do very sophisticated argument parsing very easy. – daleif May 14 '13 at 11:02
  • 1
    @ daleif: That would be far too sensible :) It looks like redefining the two commands using that package is probably the way to go. Thanks! – Tim May 14 '13 at 11:12
  • 3
    Much as I like your question I feel the title would be better if it described the symptom rather than the cause. – Matthew Leingang May 14 '13 at 12:41
  • @MatthewLeingang point taken, but I couldn't think of a concise way of describing this particular problem. – Tim May 14 '13 at 14:20

2 Answers2

9

A quick attempt with xparse:

\documentclass{article}
\usepackage{xparse,mathrsfs}
\NewDocumentCommand\optional{mO{}mm}
 {%
  \NewDocumentCommand{#1}{o#2}
   {\IfNoValueTF{##1}{#4}{#3}}%
 }
\NewDocumentCommand\starredcommand{O{s}mmm}
 {%
  \NewDocumentCommand{#2}{#1}
   {\IfBooleanTF{##1}{#4}{#3}}%
 }

\optional{\foo}{F(#1)}{F}
\optional{\baz}[m]{Baz with opt #1 and #2}{Baz without opt and #2}
\starredcommand{\barr}{\mathcal{B}}{\mathscr{B}}
\starredcommand[t{+}]{\carr}{\mathcal{C}}{\mathscr{C}}

\begin{document}
$\foo$

$\foo[x]$

$\foo[\barr*]$

$\foo[\barr]$

$\foo[\carr+]$

$\foo[\carr]$

\baz{x}

\baz[Y]{x}
\end{document}

However you have to change the way arguments are specified. You see in the example how to use a + instead of the *; for \optional and how to define \baz with a mandatory argument (the optional one is always present).

enter image description here

egreg
  • 1,121,712
  • Fantastic thank you. That might have taken days to figure out with just manual. I can probably figure out how to do the arguments using this. – Tim May 14 '13 at 11:53
5

Here is how you can have your \starredcommand with arguments

\documentclass{article}
\usepackage{amsmath}
\usepackage{mathrsfs}
\makeatletter
% Following 3 lines thanks to Prof. Enrico Gregorio, from:
% http://tex.stackexchange.com/questions/42318/
%   removing-a-backslash-from-a-character-sequence
\begingroup\lccode`\|=`\\
\lowercase{\endgroup\def\removebs#1{\if#1|\else#1\fi}}
\newcommand{\@macro@name}[1]{\expandafter\removebs\string#1}
%
\newcommand\starredcommand[5]{%
  \gdef\mname{\@macro@name{#1}}%
  \newcommand#1{\@ifstar{\csname @\mname star\endcsname}
                        {\csname @\mname\endcsname}}
  \expandafter\newcommand\csname @\mname\endcsname[#2]{#3}
  \expandafter\newcommand\csname @\mname star\endcsname[#4]{#5}
}
\makeatother
\begin{document}

\starredcommand{\barr}{1}{\mathcal #1}{1}{\mathscr #1}

\begin{align}
    \barr{C} &\neq \barr*{F}
\end{align}

\end{document}

enter image description here

FOLLOW UP: Here is a version of your \optional command:

\usepackage{ifthen}
% ...
\def\optional#1[#2]#3#4{%
  \newcommand#1[#2][]{\ifthenelse{\equal{##1}{}}{#4}{#3}}%
}

though it is not ideal. It does allow, per the user's desire, for \barr to be an argument of foo, except that the \barr must be protected, as in

\optional{\foo}[1]{F(#1)}{F}
\( \foo = \foo[\protect\barr{Q}] \neq \foo[\protect\barr*{R}]\)

enter image description here

David Carlisle
  • 757,742
  • 1
    Also excellent. I'd tried removing the backslashes but given up as impossible. I'm going to use xparse though. It's probably more stable than the code I write. – Tim May 14 '13 at 11:57