我想知道如何使用 Futurelet 通过自己的命令重新定义现有命令来确定参数的数量和类型?基本上我想做的类似于
\renewcommand{\log}[2][]{\mathrm{log}_{#1}\left( #2 \right)}
但是有一个附加选项,即用户可以使用不带参数的命令,即写入\log 5
以获得输出日志 5,因为有些人更喜欢写不带括号的对数。
这是我尝试过的
%logarithm
\newcommand{\logRM}{\mathrm{log}} %log operator
\newcommand{\logOneArg}[1]{\logRM\left(#1\right)} %\logRM()
\newcommand{\logTwoArgs}[2][]{\logRM_{#1}\left(#2\right)} %\logRM_basis()
\DeclareRobustCommand\log{\futurelet\logNext\logCheck}
\def\logCheck{%
\ifx\bgroup\logNext \expandafter\logOneArg %if \log is followed by {
\else%
\ifx[\logNext \expandafter\logTwoArgs %if \log is followed by [
\else%
\expandafter\logRM %otherwise
\fi%
\fi%
}
我遇到了两个问题。其中一个是可选参数没有像我想象的那样工作,而是\log[2]{8}
输出了 log()[2]8。另一个问题是我收到一条错误消息,因为\log
已经定义了。我试图通过将上面的命令表示为来规避这个问题,\xlog
然后尝试以下方法之一,但都没有奏效。
\def\log{\xlog}
\let\log\xlog
\renewcommand{\log}{\xlog}
\edit:
在一个问题中提出两个问题可能不太明智。因此,以下是我想要的无参数情况的信息。如果我们省略可选参数,那么
%logarithm
\newcommand{\logRM}{\mathrm{log}} %log operator
\newcommand{\logOneArg}[1]{\logRM\left(#1\right)} %\logRM()
\DeclareRobustCommand\xlog{\futurelet\logNext\logCheck}
\def\logCheck{%
\ifx\bgroup\logNext \expandafter\logOneArg %if \log is followed by {
\else%
\expandafter\logRM %otherwise
\fi%
}
定义一个如果我没有任何参数就不会有括号的命令,即我可以写出\xlog^+(x)
对数的正部分,并\xlog{\frac{x+1}{x}}
在更复杂的表达式周围使用不同大小的括号。
现在的一个问题是,即使 \log 已经定义,我如何调用此函数 \log?另一个问题是如何扩展此宏以允许额外的可选参数,例如对数的基数,因为它似乎不像我在第一篇文章中发布的那样工作。
我希望它能发挥作用,这一点已在我上面编辑的帖子中列出。它与 Futurelet 配合使用,以查看是否有任何参数,如果我们像我编辑的问题中那样省略带有可选参数的情况,那么
\xlog^+ : [1, \infty) \to \mathbb{R}_+
\xlog{\frac{x}{x+1}}
两者都产生了我们期望的输出(分别是log^+ : [1, \infty) \to R
或log(x/(x+1))
)。我想我要说的是,我希望以这样一种方式编写此函数,即如果我不写括号,LaTeX 就不会等待参数,因为不会有括号。抱歉,我弄错了括号,我不是母语人士,但至少我现在知道了。
\edit:
考虑了你关于间距的评论,并意识到概括命令的更好方法\log
当然是使用\let
,因此我们保留旧命令的所有功能并获取新命令,即使用类似这样的命令
\let\logRM\log %log operator
\newcommand{\logOneArg}[1]{\logRM\left(#1\right)} %\logRM()
%\newcommand{\logTwoArgs}[2][]{\logRM_{#1}\left(#2\right)} %\logRM_basis()
\DeclareRobustCommand\xlog{\futurelet\logNext\logCheck} %look what next character is
\def\logCheck{%
\ifx\bgroup\logNext \expandafter\logOneArg% %if \log is followed by {
\else%
%\ifx[\logNext \expandafter\logTwoArgs% %if \log is followed by [
%\else%
\expandafter\logRM %otherwise
%\fi%
\fi%
}
\renewcommand{\log}{\xlog}
现在,如果\log
后面没有任何括号,它的行为与初始的 一样\log
,因此如果我们想在 nath 中使用括号,我们仍然可以这样做。另一方面,如果我们写,\log{expression}
我们会得到括号中的表达式。现在唯一剩下的问题是如何扩展它以采用可选参数,例如对数的底数。
答案1
首先收集可选参数;然后检查^
并最后检查{
:
\documentclass{article}
\makeatletter
\let\@@log\log
\renewcommand{\log}[1][]{%
\@@log_{#1}\@ifnextchar^\log@withexp\log@maybeparen}
\def\log@withexp^#1{^{#1}\log@maybeparen}
\def\log@maybeparen{\@ifnextchar\bgroup{\log@paren}{}}
\def\log@paren#1{\mathopen{}\left(#1\right)\mathclose{}}
\makeatother
\begin{document}
$\log 2+\log[2]2$
$\log{2}+\log[2]{2}$
$\log^{+}2+\log[2]^{+}2$
$\log^{+}{2}+\log[2]^{+}{2}$
\end{document}
作为练习,这里有一个解析版本:
\usepackage{xparse}
\makeatletter
\let\@@log\log
\DeclareDocumentCommand{\log}{o t^}{
\@@log
\IfNoValueTF{#1}{}{_{#1}}
\IfBooleanTF{#2}{\log@exp}{\log@afterexp}
}
\DeclareDocumentCommand{\log@exp}{m}{^{#1}\log@afterexp}
\DeclareDocumentCommand{\log@afterexp}{g}{
\IfNoValueTF{#1}{}{\mathopen{}\left(#1\right)\mathclose{}}
}
\makeatother
答案2
(编辑:我的第一个回答误解了这个问题)
定义这种通用智能宏的基本问题是,它们必须预测您想要处理的各种情况。实际上,您必须让宏在幕后切换您能想到的所有用法。我不清楚这是否值得付出努力;例如,您希望(在评论中)能够编写\log^+{x}
并正确解析它,但您不想要写\log_2{x}
,而是提供下标作为可选参数::\log[2]{x}
再多一个字符,而且是一个尴尬的字符(无论如何,在我的键盘上)。
我可以看到其他问题。例如,您希望它注意到输入中没有给出括号或圆括号,并在输出中省略它们,因此\log 20
变成了log 20
而不是log(20)
,正如 Peter Grill 向我指出的那样。那 呢\log 100000
?对于大数字,我有时喜欢使用siunitx
来插入逗号:\log \num{100000}
。您的宏必须处理这一点。还有手写的数字符号,如\log 1e6
。您也很难识别符号\log \colon \mathbb{R}^+ \to \mathbb{R}
,除非您制作\log{}
一个特殊情况不是在空参数周围打印括号。
甚至将参数放在调整大小的括号中的基本目标也不是那么简单。如果你写 会怎么样\log((x + 1)(x + 2))
?有一种可接受的观点认为,外面的括号应该比里面的括号大,以便在视觉上区分公式的各个部分。使用\left
和无法做到这一点\right
。如果你写 ,\log(\sum_{n = 1}^\infty ...)
你会得到巨大的括号。这个nath
包(我认为)处理得更优雅。
我的观点是,当你只想做以下事情时,最好不要让计算机读懂你的想法:log
像数学运算符一样打印单词(\DeclareMathOperator
如果\log
尚未定义,我们可以使用这种方法。你还记得在自己的宏中注意空格吗?);如果代码中有基数,则放入基数(使用下标很容易做到,下标与可选参数一样是可选的);将实际参数放在调整大小的括号中,我认为这实际上是不是你想要什么。
最后,回答您修改后的帖子中提出的问题:
回答:使用\renewcommand{\log}{\xlog}
。