선택적 인수에 필수 인수를 사용합니까?

선택적 인수에 필수 인수를 사용합니까?

두 개의 인수(하나는 선택 사항)를 사용하는 명령을 정의하려고 합니다. 선택적 인수가 제공되지 않으면 선택적 인수에 필수 인수를 사용하고 싶습니다.

예를 들어:

\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의 값으로도 사용해야 한다고 말하고 있습니다 #1. 두 번째 호출에서는 선택적 인수가 제공되므로 로 대체됩니다 #1.

답변2

\NewDocumentCommandfrom 을 xparse사용하면 선택적 인수(o)가 제공되었는지 여부를 확인하는 것이 매우 쉽습니다 \IfValueTF{#1}{}{}.

그러나 에서는 그렇게 쉽지 않습니다 \newcommand. 인수 없이 명령을 정의하고 다음 문자가 a인지 \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\relaxplace #2또는 를 확인하도록 지정했습니다 #1. \ifx다음 두 토큰의 동등성을 비교합니다.

답변4

나는 egreg의 답변, 즉 \@dblarg.

Werner는 비교 측면에서 "기본값"을 확인할 것을 제안했습니다 \ifx.

기본값을 확인하는 것은 선택적 인수가 전혀 제공되지 않는 경우와 선택적 인수가 기본값으로 명시적으로 제공되는 경우를 구별하지 않기 때문에 선택적 인수가 제공되었는지 여부를 확인하는 것과 다소 다릅니다.

\ifx-비교는 다음을 의미합니다.

  • \ifx비교는 임시 매크로를 정의하고 비교하여 확장할 수 없는 방식으로 수행됩니다.
  • 또는 \ifx-comparison은 기본값을 형성하는 토큰과 실제로 선택적 인수로 제공되는 토큰에 직접 적용됩니다.

후자의 경우 \ifx-comparison은 기본값으로 사용할 수 있는 항목에 대해 일부 제한을 부과하며 완전히 "방수"되지는 않습니다.

  • 기본값은 여러 토큰으로 구성될 수 없습니다.
  • \ifx-comparison은 매크로 인수가 아닌 단일 토큰을 비교하므로 인수 \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을 선호합니다.

관련 정보