具有两个不同参数分隔符的同一命令

具有两个不同参数分隔符的同一命令

我有以下代码

\makeatletter
\def\ip#1{\@ip#1\relax}
\def\@ip#1|#2\relax{\langle#1|#2\rangle}
\makeatother

当在文档中发现时,$\ip{2|3}$他会得到类似 <2|3> 的内容。由于我有时会写,$\ip{2,3}我希望这会产生与上述相同的结果。如果可能的话,我想避免xparse

答案1

xparse在这里不会有太大帮助(而且它是内置在 LaTeX 中的,所以也没有理由避免使用它)。此代码检查是否存在,,如果不存在,则假定存在|。这有一个很好的结果,即如果两者都不存在,则命令不会出现错误Runaway argument

在此处输入图片描述

\documentclass{article}
\usepackage[T1]{fontenc}

\makeatletter
\def\ip#1{\@ipc#1,\@ipc,\relax}
\def\@ipc#1,#2,#3\relax{%
  \langle#1\ifx\@ipc#2\else|#2\fi\rangle}
\makeatother

\begin{document}
When in the document one finds $\ip{2|3}$ he gets something like <2|3>.
Since I sometimes write $\ip{2,3}$ or even just $\ip{2}$ I want that this
produces the same result as above. If possible I would like to avoid xparse.
\end{document}

以下是更严格的版本,它要求至少有一个分隔符,但使用多个分隔符也会导致错误。请注意,混合分隔符$\ip{1|2,3}$不会出错,因为宏一次只检查一个分隔符:

在此处输入图片描述

\documentclass{article}
\usepackage[T1]{fontenc}

\makeatletter
\def\@ipnil{\@ipnil}
\def\ip#1{\@ipv#1|\@ipnil|\relax}
\def\@ipcheck#1#2#3#4{%
  \ifx\@ipnil#2%
    #4\expandafter\@gobbletwo
  \else \@@ipcheck{#1}{#2}{#3}%
  \fi}
\def\@@ipcheck#1#2#3\fi#4#5{\fi
  \expandafter\ifx\@car#3\@nil\@ipnil \@ip{#1}{#2}%
  \else \@ipinvalid{#1#4#2#4#5#3}%
  \fi}
\def\@ipv#1|#2|#3\relax{%
  \@ipcheck{#1}{#2}{#3}{\@ipc#1,\@ipnil,\relax}|\@ipvclean}
\def\@ipvclean#1|\@ipnil|{#1}
\def\@ipc#1,#2,#3\relax{%
  \@ipcheck{#1}{#2}{#3}{\@ipinvalid{#1}},\@ipcclean}
\def\@ipcclean#1,\@ipnil,{#1}
\def\@ip#1#2{\langle#1|#2\rangle}
\def\@ipinvalid#1{%
  \GenericError{}{Argument '#1' invalid}\@gobble{}%
  !!#1!!}
\makeatother

\begin{document}
When in the document one finds $\ip{2|3}$ he gets something like <2|3>.
Since I sometimes write $\ip{2,3}$ I want that this produces the same
result as above. But $\ip{2}$ and $\ip{}$ should throw errors, as
should $\ip{1|2|3}$ or $\ip{1,2,3}$ or $\ip{1,2,3,4,5}$, but mixing
input syntax like $\ip{1|2,3}$ is fine. If possible I would like to
avoid xparse.
\end{document}

将该代码的复杂性与expl3执行相同操作的代码进行比较,但也比较混合输入的错误,例如$\ip{1|2,3}$

在此处输入图片描述

\documentclass{article}
\usepackage[T1]{fontenc}

\ExplSyntaxOn
\seq_new:N \l__benatti_tmp_seq
\cs_new_protected:Npn \ip #1
  {
    \regex_split:nnN { , | \| } {#1} \l__benatti_tmp_seq
    \int_compare:nNnTF { \seq_count:N \l__benatti_tmp_seq } = { 2 }
      { \langle \seq_use:Nn \l__benatti_tmp_seq { | } \rangle }
      {
        \msg_error:nnn { benatti } { invalid-arg } {#1}
        !!#1!!
      }
  }
\msg_new:nnn { benatti } { invalid-arg }
  { Argument~'#1'~invalid~for~\iow_char:N\\ip. }
\ExplSyntaxOff

\begin{document}
When in the document one finds $\ip{2|3}$ he gets something like <2|3>.
Since I sometimes write $\ip{2,3}$ I want that this produces the same
result as above. But $\ip{2}$ and $\ip{}$ should throw errors, as
should $\ip{1|2|3}$ or $\ip{1,2,3}$ or $\ip{1,2,3,4,5}$ or $\ip{1|2,3}$.
If possible I would like to avoid xparse.
\end{document}

答案2

您可以先检查|,然后检查,

\documentclass{article}
\usepackage{amsmath}

\makeatletter
\newcommand{\ip}[1]{%
  \ip@checkbar{#1}#1|\@nil%
}
\def\ip@checkbar#1#2|#3\@nil{%
  \if\relax\detokenize{#3}\relax
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
  {\ip@checkcomma{#1}#1,\@nil}%
  {\ip@bar#1\@nil}%
}
\def\ip@checkcomma#1#2,#3\@nil{%
  \if\relax\detokenize{#3}\relax
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
  {\ERROR\ip@{#1}{??}}%
  {\ip@comma#1\@nil}
}
\def\ip@bar#1|#2\@nil{\ip@{#1}{#2}}
\def\ip@comma#1,#2\@nil{\ip@{#1}{#2}}
\def\ip@#1#2{\langle #1 \mid\nobreak #2 \rangle}
\makeatother

\begin{document}

$\ip{2|3}$

$\ip{2,3}$

$\ip{2}$

\end{document}

在此处输入图片描述

我的expl3版本:

\documentclass{article}
\usepackage{amsmath}

\ExplSyntaxOn

\NewDocumentCommand{\ip}{m}
 {
  \benatti_ip_test:n { #1 }
 }

\tl_new:N \l__benatti_ip_tl
\int_new:N \l__benatti_ip_del_int

\cs_new_protected:Nn \benatti_ip:nn { \langle #1 \mid\nobreak #2 \rangle }

\cs_new_protected:Nn \benatti_ip_test:n
 {
  \regex_count:nnN { [\|\,] } { #1 } \l__benatti_ip_del_int
  \int_compare:nTF { \l__benatti_ip_del_int != 1 }
   {
    \ERROR
    \benatti_ip:nn { #1 } { ??? }
   }
   {
    \tl_set:Nn \l__benatti_ip_tl { #1 }
    \regex_replace_once:nnN
      { (.*) [\|\,] (.*) } % \1 is what's before the delimiter, \2 what's behind
      { \c{benatti_ip:nn}\{\1\}\{\2\} } % replace with \benatti_ip:nn{\1}{\2}
      \l__benatti_ip_tl
    \tl_use:N \l__benatti_ip_tl
   }
 }

\ExplSyntaxOff

\begin{document}

$\ip{2|3}$

$\ip{2,3}$

$\ip{2}$

$\ip{2|3|4}$

$\ip{2,3|4}$

\end{document}

缺少分隔符或分隔符过多都会触发错误(当然,错误消息可能会更清晰)。

在此处输入图片描述

不过,我会避免使用正则表达式,以允许嵌套调用\ip

\documentclass{article}
\usepackage{amsmath}

\ExplSyntaxOn

\NewDocumentCommand{\ip}{m}
 {
  \benatti_ip_test:n { #1 }
 }

\cs_new_protected:Nn \benatti_ip_test:n
 {
  \tl_if_in:nnTF { #1 } { | }
   {
    \benatti_ip_bar:w #1 \q_stop
   }
   {
    \tl_if_in:nnTF { #1 } { , }
     {
      \benatti_ip_comma:w #1 \q_stop
     }
     {
      \ERROR \benatti_ip:nn { #1 } { ??? }
     }
   }
 }

\cs_new_protected:Nn \benatti_ip:nn { \langle #1 \mid\nobreak #2 \rangle }
\cs_new_protected:Npn \benatti_ip_bar:w #1 | #2 \q_stop { \benatti_ip:nn { #1 } { #2 } }
\cs_new_protected:Npn \benatti_ip_comma:w #1 , #2 \q_stop { \benatti_ip:nn { #1 } { #2 } }

\ExplSyntaxOff

\begin{document}

$\ip{2|3}$

$\ip{2,3}$

$\ip{2}$

$\ip{\ip{u,v}w|z}=\ip{u|v}\ip{w|z}$

\end{document}

在此处输入图片描述

最后一个版本与第一个版本基本相同,但我认为它更加清晰。

答案3

这是一个相当简单的请求,您可以将,其更改|为易于使用。下面我使用etoolbox到 patch\ip的参数,在打印结果之前替换,为。以下是 的语法:|\patchcmd

\patchcmd{<cmd>}{<search>}{<replace>}{<success>}{<failure>}

在此处输入图片描述

\documentclass{article}

\usepackage{etoolbox}

\newcommand{\ip}[1]{%
  \def\iparg{#1}% Store
  \patchcmd{\iparg}{,}{|}{}{}% Replace , with |
  \langle \iparg \rangle% Print content
}

\begin{document}

When in the document one finds $\ip{2|3}$ he gets something like 
$\langle 2 | 3 \rangle$. Since I sometimes write $\ip{2,3}$ I want 
that this produces the same result as above.

\end{document}

此选项允许显式嵌套。例如,$\ip{\ip{2,\ip{3,4}}|\ip{5,\ip{6|7}}}$产生:

在此处输入图片描述

答案4

虽然这不是最短的解决方案,但会触发错误并避免删除花括号:

\documentclass{article}
\usepackage[T1]{fontenc}

\makeatletter
\newcommand\UD@Exchange[2]{#2#1}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral\expandafter\@secondoftwo\string{\expandafter
  \@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@secondoftwo\string}\expandafter\UD@stopromannumeral\@secondoftwo}{%
  \expandafter\UD@stopromannumeral\@firstoftwo}%
}%
\@ifdefinable\UD@gobbletocomma{\long\def\UD@gobbletocomma#1,{}}%
\@ifdefinable\UD@gobbletobar{\long\def\UD@gobbletobar#1|{}}%
\newcommand\UD@CheckWhetherNoComma[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@gobbletocomma#1,}%
}%
\newcommand\UD@CheckWhetherNoBar[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@gobbletobar#1|}%
}%
\@ifdefinable\UD@splitcomma{%
  \long\def\UD@splitcomma#1#2#3,{\expandafter\UD@Exchange\expandafter{\@firstoftwo{}#3}{#1}#2}%
}%
\@ifdefinable\UD@splitbar{%
  \long\def\UD@splitbar#1#2#3|{\expandafter\UD@Exchange\expandafter{\@firstoftwo{}#3}{#1}#2}%
}%
\newcommand\ip@syntaxerror{%
  %\PackageError{Package-Name}%
  %             {Invalid argument for \string\ip}%
  %             {Only syntax {<parameter 1>|<parameter 2>} \MessageBreak or
  %              {<parameter 1>,<parameter 2>} is allowed}%
  \GenericError{(macro \string\ip)\@spaces \@spaces \@spaces \@spaces }%
               {Macro \string\ip\space error: Invalid argument for \string\ip}%
               {See the comments for explanation.}%
               {Only syntax {<parameter 1>|<parameter 2>} \MessageBreak or
                 {<parameter 1>,<parameter 2>} is allowed}%
}%
\@ifdefinable\ip{%
  \DeclareRobustCommand\ip[1]{%
    \UD@CheckWhetherNoComma{#1}{%
      \UD@CheckWhetherNoBar{#1}{\ip@syntaxerror}{%
        \expandafter\UD@CheckWhetherNoBar
        \expandafter{\UD@gobbletobar#1}%
                    {\UD@splitbar{\left\langle}{\middle\vert}.#1\right\rangle}%
                    {\ip@syntaxerror}%
      }%
    }{%
      \UD@CheckWhetherNoBar{#1}{%
        \expandafter\UD@CheckWhetherNoComma
        \expandafter{\UD@gobbletocomma#1}%
                    {\UD@splitcomma{\left\langle}{\middle\vert}.#1\right\rangle}%
                    {\ip@syntaxerror}%
      }{\ip@syntaxerror}%
    }%
  }%
}%
\makeatother

\begin{document}

$\ip{2|3}$ 

$\ip{2,3}$ 

\message{^^J^^JPress enter to start those things that trigger errors}
\immediate\read-1 to \Scratchmacro

$\ip{}$ 

$\ip{A}$ 

$\ip{2|3|4}$ 

$\ip{2|3,4}$ 

$\ip{2,3,4}$ 

$\ip{2|3,4}$ 

\end{document}

生成的 PDF 文件

在此处输入图片描述

终端/控制台上的输出:

This is pdfTeX, Version 3.14159265-2.6-1.40.21 (TeX Live 2020) (preloaded format=pdflatex)
 restricted \write18 enabled.
entering extended mode
(./test.tex
LaTeX2e <2020-10-01> patch level 4
L3 programming layer <2021-02-18>
(/usr/local/texlive/2020/texmf-dist/tex/latex/base/article.cls
Document Class: article 2020/04/10 v1.4m Standard LaTeX document class
(/usr/local/texlive/2020/texmf-dist/tex/latex/base/size10.clo))
(/usr/local/texlive/2020/texmf-dist/tex/latex/base/fontenc.sty)
(/usr/local/texlive/2020/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def)
(./test.aux) 

Press enter to start those things that trigger errors

! Macro \ip error: Invalid argument for \ip.

See the comments for explanation.
Type  H <return>  for immediate help.
 ...                                              
                                                  
l.69 $\ip{}
           $
? 

! Macro \ip error: Invalid argument for \ip.

See the comments for explanation.
Type  H <return>  for immediate help.
 ...                                              
                                                  
l.71 $\ip{A}
            $
? 

! Macro \ip error: Invalid argument for \ip.

See the comments for explanation.
Type  H <return>  for immediate help.
 ...                                              
                                                  
l.73 $\ip{2|3|4}
                $
? 

! Macro \ip error: Invalid argument for \ip.

See the comments for explanation.
Type  H <return>  for immediate help.
 ...                                              
                                                  
l.75 $\ip{2|3,4}
                $
? 

! Macro \ip error: Invalid argument for \ip.

See the comments for explanation.
Type  H <return>  for immediate help.
 ...                                              
                                                  
l.77 $\ip{2,3,4}
                $
? 

! Macro \ip error: Invalid argument for \ip.

See the comments for explanation.
Type  H <return>  for immediate help.
 ...                                              
                                                  
l.79 $\ip{2|3,4}
                $
? 
[1{/usr/local/texlive/2020/texmf-var/fonts/map/pdftex/updmap/pdftex.map}]
(./test.aux) ){/usr/local/texlive/2020/texmf-dist/fonts/enc/dvips/cm-super/cm-s
uper-t1.enc}</usr/local/texlive/2020/texmf-dist/fonts/type1/public/amsfonts/cm/
cmr10.pfb></usr/local/texlive/2020/texmf-dist/fonts/type1/public/amsfonts/cm/cm
sy10.pfb></usr/local/texlive/2020/texmf-dist/fonts/type1/public/cm-super/sfrm10
00.pfb>
Output written on test.pdf (1 page, 18322 bytes).
Transcript written on test.log.


如果您使用非常旧的 TeX 引擎/LaTeX 2e 内核(其中\middle不可用),您可以执行以下操作:

\documentclass{article}
\usepackage[T1]{fontenc}

\makeatletter
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral\expandafter\@secondoftwo\string{\expandafter
  \@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@secondoftwo\string}\expandafter\UD@stopromannumeral\@secondoftwo}{%
  \expandafter\UD@stopromannumeral\@firstoftwo}%
}%
\@ifdefinable\UD@gobbletocomma{\long\def\UD@gobbletocomma#1,{}}%
\@ifdefinable\UD@gobbletobar{\long\def\UD@gobbletobar#1|{}}%
\newcommand\UD@CheckWhetherNoComma[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@gobbletocomma#1,}%
}%
\newcommand\UD@CheckWhetherNoBar[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@gobbletobar#1|}%
}%
\@ifdefinable\UD@splitcomma{%
  \long\def\UD@splitcomma#1#2#3,{%
    \expandafter\UD@PassFirstToSecond\expandafter{\expandafter{\@firstoftwo{}#3}#2}{#1}{}%
  }%
}%
\@ifdefinable\UD@splitbar{%
  \long\def\UD@splitbar#1#2#3|{%
    \expandafter\UD@PassFirstToSecond\expandafter{\expandafter{\@firstoftwo{}#3}#2}{#1}{}%
  }%
}%
\newcommand\ip@syntaxerror{%
  %\PackageError{Package-Name}%
  %             {Invalid argument for \string\ip}%
  %             {Only syntax {<parameter 1>|<parameter 2>} \MessageBreak or
  %              {<parameter 1>,<parameter 2>} is allowed}%
  \GenericError{(macro \string\ip)\@spaces \@spaces \@spaces \@spaces }%
               {Macro \string\ip\space error: Invalid argument for \string\ip}%
               {See the comments for explanation.}%
               {Only syntax {<parameter 1>|<parameter 2>} \MessageBreak or
                 {<parameter 1>,<parameter 2>} is allowed}%
}%
\newcommand\UD@Makeformula[2]{%
  \mathopen{\left\langle\vphantom{#1}#2\right\vert}%
  \mathclose{\left.\kern-\nulldelimiterspace\vphantom{#2}#1\right\rangle}%
}%
\@ifdefinable\ip{%
  \DeclareRobustCommand\ip[1]{%
    \UD@CheckWhetherNoComma{#1}{%
      \UD@CheckWhetherNoBar{#1}{\ip@syntaxerror}{%
        \expandafter\UD@CheckWhetherNoBar
        \expandafter{\UD@gobbletobar#1}%
                    {\UD@splitbar{\UD@splitbar{\expandafter\UD@Makeformula\@firstoftwo}}{}{}#1|}%
                    {\ip@syntaxerror}%
      }%
    }{%
      \UD@CheckWhetherNoBar{#1}{%
        \expandafter\UD@CheckWhetherNoComma
        \expandafter{\UD@gobbletocomma#1}%
                    {\UD@splitcomma{\UD@splitcomma{\expandafter\UD@Makeformula\@firstoftwo}}{}{}#1,}%
                    {\ip@syntaxerror}%
      }{\ip@syntaxerror}%
    }%
  }%
}%
\makeatother

\begin{document}

$\ip{2|3}$ 

$\ip{2,3}$ 

%$\left\langle2\middle\vert3\right\rangle$

\message{^^J^^JPress enter to start those things that trigger errors}
\immediate\read-1 to \Scratchmacro

$\ip{}$

$\ip{A}$ 

$\ip{2|3|4}$ 

$\ip{2|3,4}$ 

$\ip{2,3,4}$ 

$\ip{2|3,4}$ 

\end{document}

相关内容