
2 つの引数 (1 つはオプション) を取るコマンドを定義しようとしています。オプションの引数が指定されていない場合は、オプションの引数に必須の引数を使用するようにしたいと思います。
例えば:
\necommand{\foo}[2][#2]{#1 foo(#2)}
結果は次のようになります:
IN: \foo[hello]{hi} OUT: hello foo(hi)
IN: \foo{hi} OUT: hi foo(hi)
理想的には、シンプルにして、パッケージなしで標準の LaTeX を使用するのがよいでしょう。よろしくお願いします。
答え1
これに対する古典的なアプローチは、以下を使用することです\@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}
よりxparse
簡単になります:
\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}
オプションの引数が必要な場所には、 と入力します\IfNoValueTF{#1}{#2}{#1}
。
2017/02/10 (またはそれ以降) にリリースした場合は、より巧妙な方法がありますxparse
。オプションの引数は、O
必須の引数のいずれかをデフォルトとして受け取ることができます。
\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}
\foo
つまり、オプション引数が欠落している場合 (最初の呼び出し)、必須引数も の値として使用する必要があることを示しています。2 回目の呼び出しでは、オプション引数が指定されているため、 の代わりに#2
使用されます。#1
#1
答え2
これは、\NewDocumentCommand
を使用すると非常に簡単でxparse
、 を使用するとオプションの引数 (o) が指定されたかどうかを確認できます\IfValueTF{#1}{}{}
。
しかし、ではそう簡単ではありません\newcommand
。 引数のないコマンド、たとえば を定義し、 で次の文字が であるかどうかを\foobar
確認し、引数を使用するコマンドと、必須引数のみを使用する別のコマンド、つまりに分岐します。 この手法は「引数の移動」と呼ばれます。 この方法は他の追加パッケージを必要とせず、LaTeX コア機能を適用します。 唯一の「トリッキーな」点は、 のペアを使用することです。\@ifnextchar[{}{}
[
[]{}
{}
\makeatletter...\makeatother
\@ifnextchar[
を検索し[
、見つかった場合は、文字は基本的に「シフト」されて、\foobar@opt
オプションの引数を持つコマンドの開始として再び見つけることができるようになります(実際には、[
一時的なマクロに保存され、見つかったtrue
場合はブランチに展開されます)。[
\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}
答え3
オプションの引数にデフォルト値を指定し、これを使用して最初に指定されたかどうかを条件として使用することができます。
\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}
上記では、オプションの引数が\relax
指定されていない場合はデフォルトで になり、\ifx\relax#1\relax
place#2
または のいずれかをチェックするように指定しました#1
。は、\ifx
次の 2 つのトークンの等価性を比較します。
答え4
私は egreg の回答、つまり を使用するという提案を支持します\@dblarg
。
Werner 氏は、比較の観点から「デフォルト値」をチェックすることを提案しました\ifx
。
デフォルト値のチェックは、オプション引数がまったく提供されたかどうかのチェックとは多少異なります。オプション引数がまったく提供されていない場合と、オプション引数にデフォルト値が明示的に提供されている場合は区別されないためです。
\ifx
-比較すると
\ifx
どちらの比較も、一時的なマクロを定義して比較することによって、拡張不可能な方法で行われる。- または、
\ifx
-comparison は、デフォルト値を形成するトークンと、オプションの引数として実際に指定されたトークンに直接適用されます。
後者の場合、\ifx
-comparison はデフォルト値として使用できるものにいくつかの制限を課し、完全に「防水」ではありません。
- デフォルト値は複数のトークンで構成することはできません。
- -comparison はマクロ引数ではなく単一のトークンを比較するため
\ifx
、\ifx
引数が複数のトークンで構成されるエッジケースでは、-comparison がいくつかの誤った方法で回避される可能性があります。 - -比較は、不均衡なまたは を
\ifx
含む引数のエッジケースによっても回避される可能性があります。\else
\fi
\ifx
\let
制御シーケンス トークンまたはデフォルト値トークンに等しいアクティブ文字トークンを使用することで、比較を回避することができます。
拡張可能な方法でデフォルト値をチェックするルート、つまり一時的なマクロを定義して\ifx
比較するルートを進む場合は、
「空」をデフォルト値として設定し、空かどうかをチェックします。
\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}
または、区切られた引数を処理するマクロを使用してデフォルト値を確認します。
\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}
一時的なマクロを定義して比較するという退屈で拡張不可能な方法を取ることもできます\ifx
。
\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}
前にも言ったように、一般的な/通常の状況では、私は\@dblarg
これらのアプローチのどれよりも -thing を間違いなく好みます。