Mit \newcommand kriege ich einen Fehler, falls schon ein Makro mit dem Namen existiert. Mit \renewcommand kommt ein Fehler, falls es das noch nicht gibt. \providecommand macht ja nichts, wenn ein Makro dieses Namens schon existiert. Ich will jedoch mein Makro mit Vorrang definieren, bestehende mit zufällig gleichem Namen sind mir egal. Ich verwende eh eine eigene Namenskonvention, könnte jedoch über meine eigenen alten Makros stolpern.

Wie kann ich ein Makro (re)definieren, egal ob es schon eins dieses Namens gibt oder nicht?

gefragt 31 Aug '13, 14:58

Student's gravatar image

Student
5807779104
Akzeptiert-Rate: 82%

bearbeitet 31 Aug '13, 17:51

saputello's gravatar image

saputello
11.1k154365


Ich möchte hier zwei Möglichkeiten vorstellen, die das Gewünschte machen, aber (im Gegensatz zu Clemens’ bereits ausführlichen Antwort) Macros des LaTeX-Kernels verwenden, die das ganze Prozedere vereinfachen/verkürzen.

Version 1 gibt dabei eine Warnung aus, falls das Macro bereits definiert war, Version 2 überschreibt das zu definierende Macro, komme was wolle. In beiden Fällen müssen wir aber erst mal schauen, ob dem Command ein Asterisk * folgt, das entscheidet, ob die folgende Definition long ist. (Mehr dazu unter Was ist der Unterschied zwischen newcommand und newcommand*?

\@star@or@long

Alle Versionen von \*command und \*environment verwenden als erstes das Macro \@star@or@long, das ein Argument erwartet (das Macro, was es nach getaner Arbeit ausführen soll).

Es setzt \l@ngrel@x entweder zu \long (kein *) oder \relax (mit *). Dieses Macro wird dann in der eigentlichen Definition dem \def vorangestellt.

Soweit sehen beide Definition noch sehr ähnlich aus:

% Version 1
\newcommand*\Newcommand{\@star@or@long\New@command}

% Version 2
\newcommand*\NewCommand{\@star@or@long\New@Command}

Version 1 (mit Warnung)

Das Macro \New@command testet nun die ihm als Argument übergebene Macro-Sequenz #1 mittels \@ifundefined, dem wir das Macro aber ohne \ übergeben müssen:

  \@ifundefined{\expandafter\@gobble\string#1}{}{%
    \@latex@warning@no@line{Redefining \string#1!}}%

\@ifundefined setzt es dann mittels \csname gleich wieder zu einer Macro-Sequenz zusammen. Diese wird dann dann mithilfe von \ifx gegen \relax getestet, da alle \csnames von Macros, die undefiniert sind, bei einem \ifx-Test \relax gleichen.

Ein ähnlicher Test wäre

\ifx#1\@undefined\else\ifx#1\relax\else
   \@latex@warning@no@line{Redefining \string#1!}%
\fi\fi

der #1 erst gegen \@undefined testet (das eben nicht definiert ist) und, falls das nicht wahr ist, noch gegen \relax testet (falls #1 mit \csname erstellt wurde, siehe oben). Dieser Test findet so zum Beispiel auch von \DeclareRobustCommand verwendet, das ebenfalls das Macro überschreibt und dann eine Warnung ausgibt. Es erstellt nur eben ein „robustes“ Macro, das für Macros wichtig ist, die in „moving arguments“, also zum Beispiel \sections oder \captions verwendet wird.

Anschließend wird die „magische“ Zeile

\let\@ifdefinable\@rc@ifdefinable

ausgeführt, die kurzer Hand den eigentlichen Test der Definierbarkeit aushebelt; \@rc@ifdefinable selbst stellt die originale Definition von \@ifdefinable dann wieder her (damit es beim nächsten Mal wieder ganz normal funktioniert).

Die zu definierende Macro-Sequenz wird einfach an \new@command weitergeleitet, das dann den Rest übernimmt (Anzahl der Argumente sowie optionaler Default-Wert und eigentliche Definition).

Code

\newcommand*\Newcommand{\@star@or@long\New@command}
\newcommand*\New@command[1]{%
  \@ifundefined{\expandafter\@gobble\string#1}{}{%
    \@latex@warning@no@line{Redefining \string#1!}}%
  \let\@ifdefinable\@rc@ifdefinable
  \new@command#1}

Version 2 (ohne Warnung)

Möchte man sich die Warnung ersparen, macht man im Prinzip das Gleiche, nur lässt man den eigentlichen Test für die Warnung einfach weg und schaltet den folgenden Definierbarkeitstest mit der oben erwähnten „magischen“ Zeile ab.

Code

\newcommand*\NewCommand{\@star@or@long\New@Command}
\newcommand*\New@Command[1]{\let\@ifdefinable\@rc@ifdefinable\new@command#1}

Oder andersrum

Man könnte den Test auch kurzer Hand austricken, in dem man das zu definierende Macro #1 zu \relax lässt (siehe die Ausführung zu \csname und \@ifundefined oben):

\newcommand*\New@Command[1]{\let#1\relax\new@command#1}

Der Test wird dann aber unnötigerweise ausgeführt, mit \@rc@ifdefinable findet er aber nicht mal statt.

Das wäre ein bisschen das Gegenteil von der \providecommand-\renewcommand-Lösung, die den Test von \renewcommand auf Definiertheit aushebelt, in dem das Macro eben einfach definiert wird, falls es noch nicht existierte.

Permanenter link

beantwortet 01 Sep '13, 08:58

Qrrbrbirlbel's gravatar image

Qrrbrbirlbel
2.9k2815
Akzeptiert-Rate: 53%

bearbeitet 02 Sep '13, 06:32

Betreff

bestehende mit zufällig gleichem Namen sind mir egal.

Das halte ich für falsch: unter Umständen überschreibt man damit einen Befehl, der von einem Paket bereitgestellt wird, ohne es zu merken und nachher funktioniert dann irgendwas nicht und man muss sich erst mühsam auf die Suche nach der Ursache machen. Wenigstens sollte man sich eine Warnung oder Info in die log-Datei schreiben lassen. Hier ist ein Vorschlag, mit dem das passieren würde. Er definiert einen Befehl \definecommand mit der gleichen Syntax wie \newcommand, \renewcommand und \providecommand:

\documentclass{article}

\makeatletter
% erst mal auf optionalen Stern testen, siehe http://texwelt.de/wissen/fragen/12
% und jenachdem Version mit oder ohne Stern wählen:
\newcommand\definecommand{%
  \@ifstar
    {\define@command@star}
    {\define@command@nostar}}
% einmal mit Stern:
\newcommand\define@command@star[1]{%
  \@ifundefined{\expandafter\@gobble\string#1}
    {\newcommand*{#1}}
    {%
      \@latex@warning@no@line{Redefining \string#1!}%
      \renewcommand*{#1}%
    }}
% und einmal ohne:
\newcommand\define@command@nostar[1]{%
  \@ifundefined{\expandafter\@gobble\string#1}
    {\newcommand{#1}}
    {%
      \@latex@warning@no@line{Redefining \string#1!}%
      \renewcommand{#1}%
    }}
\makeatother

\newcommand\eins{eins}

\definecommand\eins{Eins}
\definecommand\zwei[1]{Zwei #1}
\definecommand*\vier[2][a]{Vier #2 und optional #1}

\begin{document}
% \show\befehl schreibt die Definition von \befehl ins log:
\show\eins
\show\zwei
\show\drei
\show\vier

\vier{x}
\vier[y]{x}

\end{document}

Dieses Beispiel gibt im log folgende Ausgabe:

LaTeX Warning: Redefining \eins!.

(./test.aux)
> \eins=\long macro:
->Eins.
l.36 \show\eins

> \zwei=\long macro:
#1->Zwei #1.
l.37 \show\zwei

> \drei=macro:
->Drei.
l.38 \show\drei

> \vier=macro:
->\@protected@testopt \vier \\vier {a}.
l.40 \show\vier

Es gibt also eine Warnung über die Neudefinition und auch die Stern- und die Nicht-Sterndefinition und die Parameter funktionieren wie gewünscht.

Noch eine Erklärung zur Zeile

\@ifundefined{\expandafter\@gobble\string#1}

Der Befehl \@ifundefined{<befehlsname>}{<wahr>}{<falsch>} erwartet einen Befehlsnamen ohne Backslash. Dafür wird der Befehlsname, der in #1 steckt erst einmal in eine Reihe Zeichen mit Kategorie-Code 12 verwandelt (\string#1) und vom Ergebnis das erste Zeichen mit \@gobble entfernt. Damit diese Anweisungen in der richtigen Reihenfolge geschehen, ist das \expandafter nötig.

Permanenter link

beantwortet 31 Aug '13, 15:59

cgnieder's gravatar image

cgnieder
22.1k243463
Akzeptiert-Rate: 60%

bearbeitet 31 Aug '13, 17:51

Ein einfacher Trick, der ohne \if oder ähnliche Fallunterscheidung auskommt, ist diese Kombination:

\providecommand{\name}{}
\renewcommand{\name}{...}

Zum Zeitpunkt von \renewcommand ist sichergestellt, dass das Makro \name existiert, dann wird mit der eigenen Definition ausgefüllt.

Permanenter link

beantwortet 31 Aug '13, 15:22

stefan's gravatar image

stefan ♦♦
18.1k143048
Akzeptiert-Rate: 48%

Auch wenn Clemens und Qrrbrbirlbels Antworten vorzuziehen ist, sei noch erwähnt, dass \def die von dir gewünschten Eigenschaften besitzt. Es erstellt entweder ein neues Makro oder überschreibt ein schon existierendes.

\def\name{Inhalt}

Erstellt also \name oder überschreibt ein schon existierendes. Warum es insofern gefährliche Auswirkungen haben kann, wenn etwas ohne wenn und aber überschrieben wird, zeigt dieses Beispiel:

\documentclass{scrartcl}

\def\insert{}% Kommentiere diesen Befehl aus, und die Fußnoten funktionieren wieder.
\begin{document}
Hallo\footnote{ Welt!}
\end{document}

Außerdem ist – wie @saputello in seinem Kommentar richtig bemerkte – die Definition von optionalen Argumenten mit \def recht umständlich.


Ein anderer Befehl, der auch entweder einen neuen Befehl definiert oder einen bereits vorhandenen überschreibt wäre \DeclareRobustCommand. Von der Verwendung wäre er wie \newcommand, zusätzlich wären die Befehle robust (und er überschreibt schon existierende Befehle):

\DeclareRobustCommand\name{Inhalt}

erstellt also entweder name oder überschreibt es ohne Warnung (Ich habe das Gefühl, ich wiederhole mich zuviel…).

Um das ganze kurz zu fassen: Verwende diese Befehle nur, wenn du sicher bist, dass du nichts wichtiges überschreibst!

Permanenter link

beantwortet 31 Aug '13, 16:46

Epllus's gravatar image

Epllus
620269
Akzeptiert-Rate: 46%

bearbeitet 01 Sep '13, 19:30

Allerdings ist die Definition von Befehlen mit optionalen Argumenten mit \def deutlich umständlicher als mit den LaTeX-Befehlen. Wenn ich es richtig sehe (ausprobiert habe ich es nicht) funktioniert das bei @Clemens' Lösung wie gewohnt. Ggf. sollte man außerdem wissen, dass \def einen \(re)newcommand* entspricht, während \long\def einem \(re)newcommand entspricht (sollte man aber eigentlich ohnehin wissen).

(31 Aug '13, 17:46) saputello
Deine Antwort
Vorschau umschalten

Folgen dieser Frage

Per E-Mail:

Wenn sie sich anmelden, kommen Sie für alle Updates hier in Frage

Per RSS:

Antworten

Antworten und Kommentare

Markdown-Grundlagen

  • *kursiv* oder _kursiv_
  • **Fett** oder __Fett__
  • Link:[Text](http://url.com/ "Titel")
  • Bild?![alt Text](/path/img.jpg "Titel")
  • nummerierte Liste: 1. Foo 2. Bar
  • zum Hinzufügen ein Zeilenumbruchs fügen Sie einfach zwei Leerzeichen an die Stelle an der die neue Linie sein soll.
  • grundlegende HTML-Tags werden ebenfalls unterstützt

Frage-Themen:

×52
×18

gestellte Frage: 31 Aug '13, 14:58

Frage wurde gesehen: 13,781 Mal

zuletzt geändert: 02 Sep '13, 20:18