我有以下代码
\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}