45

The preambles of my LaTeX documents often include many many lines of \DeclareMathOperator instructions, e.g.

\DeclareMathOperator{\Rep}{Rep}
\DeclareMathOperator{\Tet}{Tet}
\DeclareMathOperator{\Maps}{Maps}
\DeclareMathOperator{\Diff}{Diff}

Is there a nice way to map some macro over a list, so I could do this more concisely, and with less copying and pasting when I add a new math operator?

8 Answers 8

39

Updated solution using the features of etoolbox which has essentially implemented the “\MapCommand” and named it \forcsvlist

\documentclass{article}
\usepackage{amsmath}
\usepackage{etoolbox}

\newcommand{\DeclareMyOperator}[1]{%
  \expandafter\DeclareMathOperator\csname #1\endcsname{#1}
}
\newcommand{\DeclareMathOperators}{\forcsvlist{\DeclareMyOperator}}

\DeclareMathOperators{Rep,Tet,Maps,Diff}

\begin{document}
Operators: $\Rep, \Tet, \Maps, \Diff$
\end{document}

“For historical reasons”, I leave my original answer bellow.


Combining the previous answers from Grigory and Andrew I was able to come up with the following somewhat cleaner implementation which also exposes a nice user interface.

\documentclass{article}
\usepackage{amsmath}

\makeatletter
\def\ops@declare#1{%
  \expandafter\DeclareMathOperator\csname #1\endcsname{#1}
}
\def\ops@scan#1,{%
  \ifx#1\relax
    \let\ops@next\relax
  \else
    \ops@declare{#1}\let\ops@next\ops@scan
  \fi\ops@next
}
\newcommand{\DeclareMathOperators}[1]{\ops@scan#1,\relax,}
\makeatother

\DeclareMathOperators{Rep,Tet,Maps,Diff}

\begin{document}
Operators: $\Rep, \Tet, \Maps, \Diff$
\end{document}
6
  • Nice! (and some more characters to take me over the limit) Jul 27, 2010 at 11:59
  • The only thing I don't like about this is that it would result in lots of code duplication if I wanted to also do something like define \cX as \mathcal{X} for each letter X. Jul 27, 2010 at 15:18
  • 4
    Following the scheme above it wont be difficult to define a more general \MapCommand{\Cmd}{a,b,c} which expands to \Cmd{a}\Cmd{b}\Cmd{c}. Jul 27, 2010 at 15:33
  • 1
    @Juan: This is very useful! Unfortunately, I don't have much practice with TeX/LaTeX beyond writing my documents in it and defining the occasional macro, so I don't really understand how your solution works, and I'm not able to generalize it to something like a \MapCommand (which is what I've been trying to do to no avail). I was wondering if you could give a hint as to how to do it, or even a full implementation? Of course, I know the value of learning to do something myself, but in this case I'm sufficiently exasperated to ask for a solution that I will then try to understand. Thanks! Aug 30, 2011 at 7:23
  • 2
    @Zev: I added a new simpler solution which is more robust and hopefully much more easy to understand. It relies on the etoolbox package and its command \forcsvlist which is basically the "map command" that you are looking for. Have a look at the documentation of that package. Aug 30, 2011 at 9:13
24

There are several ways to do it using different programming facilities of different packages. Here's what it looks like using the LaTeX2e kernel command \@for to map over a comma-separated list:

\makeatletter
\newcommand\MakeMathOperators[1]{%
  \@for\@ii:=#1\do{%
    \expandafter\DeclareMathOperator\@ii
  }%
}
\makeatother
\MakeMathOperators{
  \rep  {Rep}  ,
  \tet  {Tet}  ,
  \maps {Maps}
}
3
  • This is great. I like that it's clear how to generalize this solution. Jul 27, 2010 at 15:16
  • Talking of generalising, I just generalised this to do a step-through iteration on beamer slides (x^{\step{2,3,4,5}}). I didn't know about the \@for before. Very useful! +1 Aug 27, 2010 at 18:27
  • I really wish this were the selected answer, in looking for a source about what the @ symbol is used for. This response plus saving a 100 tokens and internal LaTeX commands were key to my comprehending the role of this symbol and how to react when I run across it in others' code. Grazie! @WillRobertson
    – mpacer
    Dec 3, 2014 at 0:14
13

Combining the previous answers with the ones to the related question about scanning over a list of somethings I came up with the following solution, using etoolbox version 2.0 (2010-08-21) or later.

\newcommand{\define}[4]{\expandafter#1\csname#3#4\endcsname{#2{#4}}}
\forcsvlist{\define{\DeclareMathOperator}{}{}}{Rep, Tet, Maps, Diff}
\forcsvlist{\define{\newcommand}{\mathcal}{c}}{A,B,C,D,E,F,G,H,I,J,K}

The last line defines \cA as \mathcal{A} for all letters in the list.

If one wants a different list separator, e.g. ;, one can use \DeclareListParser*{\formylist}{;} and then replace \forcvslist with \formylist.

1
  • compact and very extensible - very nice
    – sebastian
    Nov 23, 2012 at 20:03
6

I'd probably do this differently if I were coding it today (as I've learnt a lot more about TeX in the last decade), but here's something like how I did it for my thesis:

\documentclass{article}

\usepackage{amsmath}

\makeatletter
\def\my@shift#1:#2.{#1}
\def\my@trunc#1:#2.{#2}
\def\my@void{}\relax

\newcommand{\Mops}[1]{%
 \def\my@test{}\relax
 \def\my@list{#1:}\relax
 \loop\ifx\my@test\my@void\relax
 \edef\my@elt{\expandafter\my@shift\my@list.}\relax
 \edef\my@list{\expandafter\my@trunc\my@list.}\relax
 \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\DeclareMathOperator\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter{\expandafter\expandafter\expandafter\csname \expandafter\my@elt\expandafter\endcsname\expandafter}\expandafter{\my@elt}
 \ifx\my@list\my@void
 \def\my@test{t}
 \fi
 \repeat}

\makeatother

\Mops{Det:Diff:Rep}

\begin{document}
\(\Det \Diff \Rep\)
\end{document}

The test in the loop could probably be cleaned up, and that is a rather horrendous number of \expandafters! But it works.

1
  • 1
    +1 for the last sentence about the \expandafter. Sep 8, 2014 at 8:00
3

Here's a solution from Kevin Walker's LaTeX preambles:

% tricky way to iterate macros over a list
\def\semicolon{;}
\def\applytolist#1{
    \expandafter\def\csname multi#1\endcsname##1{
        \def\multiack{##1}\ifx\multiack\semicolon
            \def\next{\relax}
        \else
            \csname #1\endcsname{##1}
            \def\next{\csname multi#1\endcsname}
        \fi
        \next}
    \csname multi#1\endcsname}

% \DeclareMathOperator{\pr}{pr} etc.
\def\declaremathop#1{\expandafter\DeclareMathOperator\csname #1\endcsname{#1}}

\applytolist{declaremathop}{pr}{im}{gl}{ev}{coinv}{tr}{rot}{Eq}{obj}{mor}{ob}{Rep}{Tet}{cat}{Maps}{Diff}{Homeo}{sign}{supp}{Nbd}{res}{rad};
3

Put the whole list of unabbreviated \DeclareMathOperator macros in a file myoperators.sty, somewhere in your latex path (I suggest ~/tex/). Then add \usepackage{myoperators} at the top of any .tex file that uses those operators.

Do this for any macro you use more than once.

3
  • 5
    I used to do this. Then I started having collaborators. Sometimes keeping everything in one file has its advantages. Jul 26, 2010 at 21:36
  • You can deal with the collaborators problem by having the files you've \input get automatically inserted into the .tex file. The LaTeX compilation system Rubber has a -o expand option, for source file expansion. Jul 27, 2010 at 5:58
  • 1
    @Willie: only as long as you ship files via email. When using version control or syncing (such as shared Dropbox folders) along with file system symlinks, using more than one file is really quite simple. Jul 27, 2010 at 7:24
1

Not quite what you're looking for, but I'd probably write the quick hack:

\newcommand{\OP}[1]{\DeclareMathOperator{#1}}
\OP{Rep}
\OP{Tet}

and so forth. Moreover if I were to use these a lot, I'd just write them once and include them anywhere.

1

E.g.

\def\deflist#1{\ifx#1\stop\let\next=\kill\else\let\next=\parse\fi\next{#1}}  
\def\kill#1{\relax}
\def\parse#1#2{\newcommand{#1}{\operatorname{#2}}\deflist}

\deflist
\holim{holim}
\hocolim{hocolim}
\stop

works (although I can't recommend using such tricks).

You must log in to answer this question.

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