Optionale Parameter in eigenen Befehlen

Redefinition von Makros, Definition eigener Befehle sowie neuer Umgebungen


IrenaSekuta
Forum-Century
Forum-Century
Beiträge: 230
Registriert: Di 2. Dez 2008, 10:05

Optionale Parameter in eigenen Befehlen

Beitrag von IrenaSekuta »

Hallo zusammen,

ich möchte mir einen eigene Befehl konstruieren, um auf das Literaturverzeichnis zuzugreifen:


Code: Alles auswählen

\documentclass[ngerman]{scrbook}
\usepackage[T1]{fontenc}
\usepackage[ansinew]{inputenc}
\usepackage[ngerman]{babel}
\usepackage{cite}
\usepackage{xspace}

\newcommand*{\Quelle}[2]{\xspace{}\cite[S.~#1]{#2}\xspace{}}


\begin{document}

\begin{thebibliography}{Literatur}

Hier wird nun auf \Quelle{3}{Lit1} referenziert und hier nun auf \cite{Lit2}.

\bibitem{Li1} Erste Literaturstelle
\bibitem{Li2} zweiteLiteraturstelle

\end{thebibliography}


\end{document}
In dem Satz habe ich \cite{Lit2}geschrieben, weil ich hier einfach auf die Literatur ohne irgendwelche Seitenangaben referenzieren will. Lieber wäre mit aber, wenn ich z.B. \Quelle{}{Lit2} schreiben könnte. Das bedeutet, wenn der erste Paramter übergeben wird soll der Befehl aussehen wie

Code: Alles auswählen

\newcommand*{\Quelle}[2]{\xspace{}\cite[S.~#1]{#2}\xspace{}}
Wenn der erste Parameter nicht übergeben wird, dann eben so:

Code: Alles auswählen

\newcommand*{\Quelle}[1]{\xspace{}\cite{#1}\xspace{}}
Hat jemand einen Tipp?

Gruß,

Irena

cliffhanger
Forum-Century
Forum-Century
Beiträge: 137
Registriert: Di 25. Aug 2009, 11:25
Wohnort: Regensburg

Beitrag von cliffhanger »

Hi Irina,

das Stichwort heißt "Optionales Argument". Vielleicht hilft dir der folgende Link

http://de.wikibooks.org/wiki/LaTeX-Wört ... newcommand

(Die Forensoftare scheint von Umlauten in URLs nicht so begeistert zu sein. Also bitte kopieren und per Hand in die Adresszeile einfügen.)
ja schonmal weiter?

Gruß
Benedikt

IrenaSekuta
Forum-Century
Forum-Century
Beiträge: 230
Registriert: Di 2. Dez 2008, 10:05

Beitrag von IrenaSekuta »

Hallo Benedikt,

danke für den Link, jedoch gestalltet sich das Problem bei mir etwas schwieriger.

Ich will ja das die komplette Zeichenkette

Code: Alles auswählen

[S.~#1]
in

Code: Alles auswählen

\newcommand*{\Quelle}[2]{\xspace{}\cite[S.~#1]{#2}\xspace{}}
weggelassen wird, wenn #1 nicht übergeben wird.

Geht denn so etwas nicht mit einer Art If-Abfrage:

Gruß,

Irena[/code]

phi
Moderator
Moderator
Beiträge: 420
Registriert: Fr 6. Feb 2009, 21:28

Beitrag von phi »

etwa so:

Code: Alles auswählen

\makeatletter
\newcommand*{\Quelle}{%
  \@ifnextchar[\Quell@\Qu@lle
}
\def\Qu@lle#1{\cite{#1}\xspace}
\def\Quell@[#1]#2{\cite[S.~#1]{#2}\xspace}
\makeatother

IrenaSekuta
Forum-Century
Forum-Century
Beiträge: 230
Registriert: Di 2. Dez 2008, 10:05

Beitrag von IrenaSekuta »

Hallo phi,

herzlichen Dank für deinen Code, aber leider funktioniert der Code nur teilweise..

Anbei das Minimalbeispiel:

Code: Alles auswählen

\documentclass[ngerman]{scrbook}
\usepackage[T1]{fontenc}
\usepackage[ansinew]{inputenc}
\usepackage[ngerman]{babel}
\usepackage{cite}
\usepackage{xspace}

\makeatletter
\newcommand*{\Quelle}{%
  \@ifnextchar[\Quell@\Qu@lle
}
\def\Qu@lle#1{\cite{#1}\xspace}
\def\Quell@[#1]#2{\cite[S.~#1]{#2}\xspace}
\makeatother


\begin{document}


Hier wird nun auf \Quelle{3}{Lit1} referenziert und hier nun auf \Quelle{Lit2}.

\begin{thebibliography}{Literatur}
\bibitem[Literaturstelle 1]{Lit1} Erste Literaturstelle
\bibitem[Literaturstelle 2]{Lit2}zweiteLiteraturstelle
\end{thebibliography}
\end{document} 
Ungeachtet dessen verstehe ich absolut nicht, was der Code machen soll.

Eventuell können wir den Code gemeinsam auseinander nehmen, sodass ich auch dabei lernen kann, wie man in Tex programmiert:

Code: Alles auswählen

\newcommand*{\Quelle}{
  \@ifnextchar[\Quell@\Qu@lle}
Handelt es sich hier um einen Tippfehler? Du eröffnest nach @ifnextchar mit einer "[" schließt diese Klammer jedoch nie.

Gruß,

Irena[/quote]

phi
Moderator
Moderator
Beiträge: 420
Registriert: Fr 6. Feb 2009, 21:28

Beitrag von phi »

IrenaSekuta hat geschrieben:Hallo phi,

herzlichen Dank für deinen Code, aber leider funktioniert der Code nur teilweise..

Anbei das Minimalbeispiel:

Code: Alles auswählen

\documentclass[ngerman]{scrbook}
\usepackage[T1]{fontenc}
\usepackage[ansinew]{inputenc}
\usepackage[ngerman]{babel}
\usepackage{cite}
\usepackage{xspace}

\makeatletter
\newcommand*{\Quelle}{%
  \@ifnextchar[\Quell@\Qu@lle
}
\def\Qu@lle#1{\cite{#1}\xspace}
\def\Quell@[#1]#2{\cite[S.~#1]{#2}\xspace}
\makeatother


\begin{document}


Hier wird nun auf \Quelle{3}{Lit1} referenziert und hier nun auf \Quelle{Lit2}.

\begin{thebibliography}{Literatur}
\bibitem[Literaturstelle 1]{Lit1} Erste Literaturstelle
\bibitem[Literaturstelle 2]{Lit2}zweiteLiteraturstelle
\end{thebibliography}
\end{document} 
Hallo,
die Syntax des \Quelle-Befehls entspricht den normalen Befehlen mit optionalem Argument, d.h. das optionale Argument muss in eckige Klammern:
\Quelle[3]{Lit1}
Eine Syntax mit geschweiften Klammern wäre möglich, aber schwieriger zu realisieren und inkonsistent mit der Syntax der übrigen LaTeX-Befehle.

Ungeachtet dessen verstehe ich absolut nicht, was der Code machen soll.

Eventuell können wir den Code gemeinsam auseinander nehmen, sodass ich auch dabei lernen kann, wie man in Tex programmiert:

Code: Alles auswählen

\newcommand*{\Quelle}{
  \@ifnextchar[\Quell@\Qu@lle}
Handelt es sich hier um einen Tippfehler? Du eröffnest nach @ifnextchar mit einer "[" schließt diese Klammer jedoch nie.
Nein. Der Befehl \@ifnextchar aus dem LaTeX-Kernel erwartet drei Argumente:
1. ein einzelnes Token (z.B. ein Zeichen), auf das getestet werden soll,
2. der Code, der ausgeführt werden soll, wenn das nächste Zeichen dem ersten Argument entspricht,
3. der Code, der andernfalls ausgeführt werden soll.
In der Regel ist das erste Argument ein einzelnes Zeichen (hier "[") und die zwei anderen Argumente sind Makros, die die restlichen Argumente aufsammeln.
In diesem Fall wird das Makro \Qu@lle aufgerufen, wenn keine eckige Klammer folgt, ansonsten das Makro \Quell@. Diese sammeln die Argumente ihrer Syntax entsprechend auf (siehe beispielsweise das Kapitel über Makros in TeX by Topic) und expandieren zu den gewünschten Ergebnissen. Die Technik, die \@ifnextchar verwendet, wird in Abschnitt 11.9.3 von TeX by Topic beschrieben.

IrenaSekuta
Forum-Century
Forum-Century
Beiträge: 230
Registriert: Di 2. Dez 2008, 10:05

Beitrag von IrenaSekuta »

Hallo phi,

recht herzlichen Dank für deine tolle Erklärung. Habe deinen Code in eine für mich etwas lesbarere Fassung gebracht:

Code: Alles auswählen

\makeatletter
\newcommand*{\Quelle}{%
  \@ifnextchar{[}{\Quell@}{\Qu@lle}
}
\def\Qu@lle#1{\cite{#1}\xspace}
\def\Quell@[#1]#2{\cite[S.~#1]{#2}\xspace}
\makeatother
Eventuell kannst Du mir noch helfen, den Code vollständig zu verstehen.

Durch probieren habe ich herausgefunden, dass folgende Zeilen Code quasi dasselbe tun:

Code: Alles auswählen

\def\Qu@lle#1{\cite{#1}\xspace}
und
\newcommand{\Qu@lle}[1]{\cite{#1}\xspace}
Aber komischerweise funktioniert es bei folgender Codeänderung nicht:

Code: Alles auswählen

\def\Quell@[#1]#2{\cite[S.~#1]{#2}\xspace}
\newcommand{\Quell@}[2]{\cite[S.~#1]{#2}\xspace}
Warum funktioniert der zweite von mir erstellte Code nicht?

Und noch eine allgemeine Frage:

Hat deine Schreibweise gegenüber meiner irgendwelche Vor-/Nachteile oder ist dein Code "besser", weil er eventuell andere Probleme vermeidet, die ich momentan aufgrund meines rudimentären Latex-Programmier-Wissens nicht erkennen?

Liebe Grüße,

Irena

phi
Moderator
Moderator
Beiträge: 420
Registriert: Fr 6. Feb 2009, 21:28

Beitrag von phi »

Hallo,

ich habe den Beitrag leider erst jetzt entdeckt, antworte aber trotzdem, vielleicht nützt es noch was.
IrenaSekuta hat geschrieben:Habe deinen Code in eine für mich etwas lesbarere Fassung gebracht:

Code: Alles auswählen

\makeatletter
\newcommand*{\Quelle}{%
  \@ifnextchar{[}{\Quell@}{\Qu@lle}
}
\def\Qu@lle#1{\cite{#1}\xspace}
\def\Quell@[#1]#2{\cite[S.~#1]{#2}\xspace}
\makeatother
Vorsicht vor solchen "Verbesserungen", wenn du nicht genau verstehst, was vor sich geht! In diesem Fall funktioniert das zwar, aber auch nur aufgrund eines Implementierungsdetails von \@ifnextchar. Ich würde deshalb raten, weiterhin meine ursprüngliche Version zu benutzen.
Durch probieren habe ich herausgefunden, dass folgende Zeilen Code quasi dasselbe tun:

Code: Alles auswählen

\def\Qu@lle#1{\cite{#1}\xspace}
und
\newcommand{\Qu@lle}[1]{\cite{#1}\xspace}
Ja, diese Definitionen sind nahezu völlig äquivalent. Die einzigen Unterschiede sind, dass der zweite Code auch Absatzenden als Argument zulässt (was in der Regel nicht erwünscht ist und mit \newcommand* verhindert werden kann), und dass ein eventuell schon vorhandener Befehl nicht überschrieben wird.
Aber komischerweise funktioniert es bei folgender Codeänderung nicht:

Code: Alles auswählen

\def\Quell@[#1]#2{\cite[S.~#1]{#2}\xspace}
\newcommand{\Quell@}[2]{\cite[S.~#1]{#2}\xspace}
Warum funktioniert der zweite von mir erstellte Code nicht?
Weil die Syntax anders ist. \Quell@ erwartet genau eine öffnende eckige Klammer, einen Parameter, eine schließende eckige Klammer und noch einen Parameter. Dies lässt sich mit \newcommand nicht darstellen.
Und noch eine allgemeine Frage:

Hat deine Schreibweise gegenüber meiner irgendwelche Vor-/Nachteile oder ist dein Code "besser", weil er eventuell andere Probleme vermeidet, die ich momentan aufgrund meines rudimentären Latex-Programmier-Wissens nicht erkennen?
Generell sollte man auf der obersten möglichen Ebene arbeiten, d.h. \newcommand benutzen. Da dies hier nicht möglich ist, muss man auf TeX-Primitven zurückgreifen. (Das experimentelle xparse-Paket bietet weitere Möglichkeiten, mit denen sich auch Befehle wie der hier besprochene sehr komfortabel definieren lassen.)
Generell sollte man nicht erwarten, bei internen Befehlen wie \@ifnextchar (erkennbar am @ im Befehlsnamen) Klammer nach Belieben weglassen oder hinzufügen zu können.
Essentiell ist die Beachtung von Leerzeichen: TeX fügt bei fast jedem Zeilenende (außer dieser beendet einen Absatz oder ein Steuernamenstoken ist das letzte Token auf der Zeile) ein Leerzeichen ein, egal ob im Text oder in Befehlsdefinitionen. Deswegen sollten die problematischen Zeilenumbrüche in Befehlsdefinitionen mit einem Kommentarzeichen maskiert werden (so wie ich das bei meinen Definitionen auch gemacht habe).

phi
Moderator
Moderator
Beiträge: 420
Registriert: Fr 6. Feb 2009, 21:28

Beitrag von phi »

Hier noch die äquivalente Version mit xparse:

Code: Alles auswählen

\usepackage{etex}
\usepackage{xparse}
\ExplSyntaxOn
% mit \ExplSyntaxOn können fast beliebig Leerzeichen und Zeilenumbrüche eingefügt werden
\NewDocumentCommand \Quelle { o m } {
  \IfValueTF { #1 } {
    \cite [ S. \nobreakspace #1 ] { #2 } % ~ wird hier als normales Leerzeichen verwendet, für ein
                                         % Leerzeichen ohne Umbruch muss deshalb \nobreakspace eingesetzt werden
  } {
    \cite { #2 }
  }
}
\ExplSyntaxOff
Auch andere Varianten sind denkbar, etwa:

Code: Alles auswählen

\usepackage{etoolbox}
\newrobustcmd*{\Quelle}[2][]{%
  \ifstrempty{#1}{%
    \cite{#2}%
  }{%
    \cite[S.~#1]{#2}%
  }%
}
Diese Variante unterscheidet sich allerdings von den anderen durch ihre Behandlung eines leeren optionalen Arguments.

Antworten