Das obligatorische Argument für das optionale Argument verwenden?

Das obligatorische Argument für das optionale Argument verwenden?

Ich versuche, einen Befehl zu definieren, der zwei Argumente annimmt, von denen eines optional ist. Wenn das optionale Argument nicht angegeben ist, möchte ich, dass das obligatorische Argument für das optionale Argument verwendet wird.

Zum Beispiel:

\necommand{\foo}[2][#2]{#1 foo(#2)}

was zurückgeben würde:

IN: \foo[hello]{hi}      OUT: hello foo(hi)
IN: \foo{hi}             OUT: hi foo(hi)

Idealerweise wäre es schön, es einfach zu halten und Standard-LaTeX ohne Pakete zu verwenden. Vielen Dank im Voraus!

Antwort1

Der klassische Ansatz hierfür ist die Verwendung von \@dblarg:

\documentclass{article}

\makeatletter
\newcommand{\foo}{\@dblarg\ah@foo}
\def\ah@foo[#1]#2{#1 foo(#2)}
\makeatother

\begin{document}

No optional argument: \foo{xyz}

Optional argument: \foo[abc]{xyz}

\end{document}

Bildbeschreibung hier eingeben

Mit xparsegeht’s einfacher:

\documentclass{article}
\usepackage{xparse}

\NewDocumentCommand{\foo}{om}{%
  \IfNoValueTF{#1}{#2}{#1} foo(#2)%
}

\begin{document}

No optional argument: \foo{xyz}

Optional argument: \foo[abc]{xyz}

\end{document}

Wo immer Sie das optionale Argument benötigen, geben Sie ein \IfNoValueTF{#1}{#2}{#1}.

xparseWenn Sie den 10.02.2017 (oder später) veröffentlicht haben, gibt es einen viel eleganteren Weg : Ein optionales Argument Okann als Standard jedes der obligatorischen Argumente annehmen:

\documentclass{article}
\usepackage{xparse}

\NewDocumentCommand{\foo}{ O{#2} m }{%
  #1 foo(#2)%
}

\begin{document}

No optional argument: \foo{xyz}

Optional argument: \foo[abc]{xyz}

\end{document}

Wir sagen also \foo, dass, wenn das optionale Argument fehlt (erster Aufruf), das obligatorische Argument #2auch als Wert für verwendet werden soll #1. Beim zweiten Aufruf wird das optionale Argument angegeben, sodass es für ersetzt wird #1.

Antwort2

Dies ist ziemlich einfach mit \NewDocumentCommandvon xparse, indem mit überprüft wird, ob das optionale Argument (o) angegeben wurde oder nicht \IfValueTF{#1}{}{}.

Mit ist das allerdings nicht so einfach \newcommand. Definieren Sie einen Befehl ohne Argumente, sagen wir \foobarund prüfen Sie mit , \@ifnextchar[{}{}ob das nächste Zeichen ein ist [, und verzweigen Sie in einen Befehl, der Argumente verwendet []{}, und einen anderen, der nur das obligatorische Argument verwendet, also {}. Diese Technik wird „Verschieben von Argumenten“ genannt. Dieser Weg benötigt keine weiteren zusätzlichen Pakete und wendet die Kernfunktionen von LaTeX an. Der einzige „knifflige“ Punkt ist die Verwendung des \makeatletter...\makeatotherPaares.

\@ifnextchar[sucht [und wenn dieses gefunden wird, wird das Zeichen grundsätzlich zurück „verschoben“, so dass \foobar@optes als Anfang eines Befehls mit optionalem Argument wiedergefunden werden kann ( [wird tatsächlich in einem temporären Makro gespeichert und für den trueZweig erweitert, wenn [es gefunden wurde)

\documentclass{article}

\usepackage{xparse}

\makeatletter
\newcommand{\foobar}{%
  \@ifnextchar[{\foobar@opt}{\foobar@noopt}
}{}

\newcommand{\foobar@opt}[2][]{%
  #1 foo(#2)%
}

\newcommand{\foobar@noopt}[1]{%
  #1 foo(#1)%
}
\makeatother

\NewDocumentCommand{\foo}{om}{%
  \IfValueTF{#1}{%
    #1 foo(#2)%
  }{%
    #2 foo(#2)%
  }%
}


\begin{document}
\foo[hello]{hi}

\foo{hi}       


\foobar[hello]{hi}

\foobar{hi}       

\end{document}

Bildbeschreibung hier eingeben

Antwort3

Sie können einen Standardwert für das optionale Argument angeben und diesen verwenden, um davon abhängig zu machen, ob das Argument ursprünglich angegeben wurde oder nicht.

Bildbeschreibung hier eingeben

\documentclass{article}

\newcommand{\foo}[2][\relax]{%
  \ifx\relax#1\relax #2\else #1\fi
  ~foo(#2)%
}

\begin{document}

\foo[hello]{hi}

\foo[]{hi}

\foo{hi}       

\end{document}

Oben habe ich angegeben, dass das optionale Argument standardmäßig verwendet wird \relax, wenn es nicht angegeben ist. Anschließend wird \ifx\relax#1\relaxentweder die Platzierung #2oder überprüft #1. \ifxVergleicht die folgenden beiden Token auf Gleichwertigkeit.

Antwort4

Ich bin für die Antwort von egreg, also den Vorschlag, zu verwenden \@dblarg.

Werner schlug vor, im Rahmen eines -Vergleichs nach einem „Standardwert“ zu suchen \ifx.

Die Überprüfung auf einen Standardwert unterscheidet sich etwas von der Überprüfung, ob überhaupt ein optionales Argument angegeben wurde, da es keinen Unterschied zwischen dem Fall gibt, in dem das optionale Argument überhaupt nicht angegeben wurde, und dem Fall, in dem das optionale Argument explizit mit dem Standardwert angegeben wurde.

\ifx-Vergleich impliziert, dass

  • Der Vergleich muss in einer nicht erweiterbaren Weise erfolgen, indem \ifxtemporäre Makros definiert und verglichen werden.
  • oder \ifx-Vergleich ist direkt auf das Token anzuwenden, das den Standardwert bildet, und das/die tatsächlich als optionales Argument übergebene(n) Token.

Im letzteren Fall \ifxerlegt -comparison einige Einschränkungen auf, was als Standardwert verwendet werden kann, und ist nicht vollständig „wasserdicht“:

  • Standardwerte können nicht aus mehreren Token bestehen.
  • Da \ifx-comparison einzelne Token und keine Makroargumente vergleicht, \ifxkann -comparison in Grenzfällen, in denen die Argumente aus mehr als einem Token bestehen, auf verschiedene fehlerhafte Weisen ausmanövriert werden.
  • Ein \ifx-Vergleich kann auch durch den Grenzfall von Argumenten, die unausgeglichene \elseoder enthalten, ausmanövriert werden \fi.
  • \ifx-Vergleiche können durch die Verwendung von Steuersequenz-Token oder aktiven Zeichen-Token, die \letdem Standardwert-Token entsprechen, ausmanövriert werden.

Wenn Sie den Weg gehen, auf erweiterbare Weise nach einem Standardwert zu suchen, d. h. keine temporären Makros zu definieren und \ifxsie zu vergleichen, schlage ich vor

  • entweder mit „Leere“ als Standardwert und Prüfung auf Leere:

    \documentclass{article}
    \makeatletter
    %%=========================================================================
    %% Paraphernalia:
    %%    \UD@firstoftwo, \UD@secondoftwo
    %%.........................................................................
    \newcommand\UD@firstoftwo[2]{#1}%
    \newcommand\UD@secondoftwo[2]{#2}%
    %%-------------------------------------------------------------------------
    %% Check whether argument is empty:
    %%.........................................................................
    %% \UD@CheckWhetherNull{<Argument which is to be checked>}%
    %%                     {<Tokens to be delivered in case that argument
    %%                       which is to be checked is empty>}%
    %%                     {<Tokens to be delivered in case that argument
    %%                       which is to be checked is not empty>}%
    %%
    %% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
    %% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
    %%
    %% A concern in his posting is that the argument is hit with \string
    %% after some expansions which in edge cases might result in unbalancing
    %% surrounding \if..\fi-constructs if the macro is used inside of such
    %% \if..\fi-constructs.
    %%
    %% That challenging concern sickened me. ;-)
    %%
    %% Therefore I decided to implerment a variant where this cannot happen
    %% as expansion is forced by \romannumeral:
    %%
    %% After the first expansion-step, \string is not applied yet.
    %% After the second expansion-step, any possibly disturbing remainders
    %% are already removed due to \romannumeral-expansion.
    %%
    %% No eTeX- or whatsoever extensions. No \if.. .Only \romannumeral,
    %% digit 0, space token for terminating \romannumeral-expansion,
    %% \string, \expandafter, \UD@firstoftwo, \UD@secondoftwo, {, }.
    %%
    %% May 20, 2016
    %%
    %% Ulrich Diez (e-mail: [email protected])
    %%
    \newcommand\UD@CheckWhetherNull[1]{%
      \romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
      \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
      \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
      \UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
      \UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
    }%
    
    \newcommand{\foo}[2][]{%
      \UD@CheckWhetherNull{#1}{#2}{#1}~foo(#2)%
    }%
    
    \makeatother
    
    \parindent=0ex
    \parskip=\bigskipamount
    
    \begin{document}
    
    Empty optional argument  or no optional argument will be given---%
    \verb|\foo{hi}|:\\
    \foo{hi}
    
    Empty optional argument  or no optional argument will be given---%
    \verb|\foo[]{hi}|:\\
    \foo[]{hi}
    
    Empty optional argument  or no optional argument will be given---%
    \verb|\foo[{}]{hi}|:\\
    \foo[{}]{hi}
    
    A nice optional argument will be given---%
    \verb|\foo[hello]{hi}|:\\
    \foo[hello]{hi}
    
    \end{document}
    

    Bildbeschreibung hier eingeben

  • oder die Überprüfung des Standardwertes mittels Makros, die durch Trennzeichen getrennte Argumente verarbeiten:

    \documentclass{article}
    \makeatletter
    %%=========================================================================
    %% Paraphernalia:
    %%    \UD@firstoftwo, \UD@secondoftwo
    %%.........................................................................
    \newcommand\UD@firstoftwo[2]{#1}%
    \newcommand\UD@secondoftwo[2]{#2}%
    %%-------------------------------------------------------------------------
    %% Check whether argument is empty:
    %%.........................................................................
    %% \UD@CheckWhetherNull{<Argument which is to be checked>}%
    %%                     {<Tokens to be delivered in case that argument
    %%                       which is to be checked is empty>}%
    %%                     {<Tokens to be delivered in case that argument
    %%                       which is to be checked is not empty>}%
    %%
    %% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
    %% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
    %%
    %% A concern in his posting is that the argument is hit with \string
    %% after some expansions which in edge cases might result in unbalancing
    %% surrounding \if..\fi-constructs if the macro is used inside of such
    %% \if..\fi-constructs.
    %%
    %% That challenging concern sickened me. ;-)
    %%
    %% Therefore I decided to implerment a variant where this cannot happen
    %% as expansion is forced by \romannumeral:
    %%
    %% After the first expansion-step, \string is not applied yet.
    %% After the second expansion-step, any possibly disturbing remainders
    %% are already removed due to \romannumeral-expansion.
    %%
    %% No eTeX- or whatsoever extensions. No \if.. .Only \romannumeral,
    %% digit 0, space token for terminating \romannumeral-expansion,
    %% \string, \expandafter, \UD@firstoftwo, \UD@secondoftwo, {, }.
    %%
    %% May 20, 2016
    %%
    %% Ulrich Diez (e-mail: [email protected])
    %%
    \newcommand\UD@CheckWhetherNull[1]{%
      \romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
      \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
      \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
      \UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
      \UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
    }%
    %%-------------------------------------------------------------------------
    %% Check whether argument contains no exclamation-mark on top-brace-level:
    %%.........................................................................
    %% \UD@CheckWhetherNoExclamationMark{<Argument which is to be checked>}%
    %%                  {<Tokens to be delivered in case that
    %%                    argument which is to be checked does not contain !>}%
    %%                  {<Tokens to be delivered in case that
    %%                    argument which is to be checked does contain !>}%
    \long\def\UD@RemoveToExclamationMark#1!{}%
    \long\def\UD@CheckWhetherNoExclamationMark#1{%
      \expandafter\UD@CheckWhetherNull\expandafter{\UD@RemoveToExclamationMark#1!}%
    }%
    %%-------------------------------------------------------------------------
    %% Fork depending on some tokens:
    %%.........................................................................
    %%\\CheckWhetherDefault{<Argument which is to be checked>}%
    %%           {<Tokens to be delivered in case argument is "Default value">}%
    %%           {<Tokens to be delivered in case argument is not "Default value">}%
    %%
    %% In case <Argument which is to be checked> is neither "case 1" nor
    %% "case 2" the phrase "Error: Unknown parameter ``<Argument which is
    %% to be checked>'' to \CheckWhetherDefault." will be delivered.
    %%
    \newcommand\@CheckWhetherDefault{}%
    \long\def\@CheckWhetherDefault%
       #1!!Default value!#2#3!!!!{#2}%
    \newcommand\CheckWhetherDefault[1]{%
      \romannumeral0%
        \UD@CheckWhetherNoExclamationMark{#1}{%
          \@CheckWhetherDefault
          !#1!Default value!{\UD@firstoftwo}% <- #1 is empty.
          !!#1!{\UD@firstoftwo}% <- #1 = Default value
          !!Default value!{\UD@secondoftwo}% <- #1 = something else without exclamation mark
          !!!!%
        }{\UD@secondoftwo}% <- #1 = something else with exclamation mark
    }%
    \makeatother
    
    \newcommand{\foo}[2][Default value]{%
      \CheckWhetherDefault{#1}{#2}%
                              {#1}%
      ~foo(#2)%
    }%
    
    \parindent=0ex
    \parskip=\bigskipamount
    
    \begin{document}
    
    ``Default value'' or empty optional argument  or no optional argument will be given---%
    \verb|\foo{hi}|:\\
    \foo{hi}
    
    ``Default value'' or empty optional argument  or no optional argument will be given---%
    \verb|\foo[Default value]{hi}|:\\
    \foo[Default value]{hi}
    
    ``Default value'' or empty optional argument  or no optional argument will be given---%
    \verb|\foo[]{hi}|:\\
    \foo[]{hi}
    
    ``Default value'' or empty optional argument  or no optional argument will be given---%
    \verb|\foo[{}]{hi}|:\\
    \foo[{}]{hi}
    
    A nice optional argument will be given---%
    \verb|\foo[hello]{hi}|:\\
    \foo[hello]{hi}
    
    \end{document}
    

    Bildbeschreibung hier eingeben

\ifxSie können auch den langweiligen, nicht erweiterbaren Weg gehen und temporäre Makros definieren und vergleichen:

\documentclass{article}

\makeatletter
\newcommand{\foo}[2][Default value]{%
  \begingroup
  \def\mytempa{#1}%
  \def\mytempb{Default value}%
  \ifx\mytempa\mytempb
    \expandafter\endgroup\expandafter\@secondoftwo
  \else
    \expandafter\@firstofone
  \fi
  {%
    \def\mytempb{}%
    \expandafter\endgroup\ifx\mytempa\mytempb
      \expandafter\@secondoftwo
    \else
      \expandafter\@firstoftwo
    \fi
    {#1}%
  }{#2}~foo(#2)%
}%
\makeatother

\parindent=0ex
\parskip=\bigskipamount

\begin{document}

``Default value'' or empty optional argument  or no optional argument will be given---%
\verb|\foo{hi}|:\\
\foo{hi}

``Default value'' or empty optional argument  or no optional argument will be given---%
\verb|\foo[Default value]{hi}|:\\
\foo[Default value]{hi}

``Default value'' or empty optional argument  or no optional argument will be given---%
\verb|\foo[]{hi}|:\\
\foo[]{hi}

``Default value'' or empty optional argument  or no optional argument will be given---%
\verb|\foo[{}]{hi}|:\\
\foo[{}]{hi}

A nice optional argument will be given---%
\verb|\foo[hello]{hi}|:\\
\foo[hello]{hi}

\end{document}

Bildbeschreibung hier eingeben

Wie ich bereits sagte: In normalen/üblichen Situationen bevorzuge ich das \@dblarg-Ding definitiv gegenüber jedem dieser Ansätze.

verwandte Informationen