假设我有一个非常复杂的函数,它依赖于i
和j
。所以我定义了一个新命令,比如
\newcommand{\foo}[2]{X_{i+j+1}}. % This might be an complicated expression, but I want to keep it simple
\foo{\alpha}{\beta}
使用这个定义,我可以像或一样调用这个命令\foo{2}{3}
。现在,我想要得到的是,如果 两个参数都是数值,我想强制它们计算这个表达式的值,即\foo{2}{3}
应该生成X_{6}
而不是X_{2+3+1}
。有时即使我知道完整的表达式,我也必须手动简化它,这很烦人。
我实际上觉得这似乎几乎不可能,但无论如何我还是想问一下是否存在一些解决方法。
答案1
这似乎是一个工作l3regex
:
\documentclass{article}
\usepackage{xparse,l3regex}
\ExplSyntaxOn
\NewDocumentCommand{\evaluateorprint}{m}
{
\regex_match:nnTF { [^0-9+\-] } { #1 }
{ #1 } % symbolic expression
{ \int_eval:n { #1 } } % only numbers, + or -
}
\ExplSyntaxOff
\newcommand\foo[2]{%
X_{\evaluateorprint{#1+#2+1}}%
}
\begin{document}
\[
\foo{2}{3}+\foo{\alpha}{\beta}
\]
\end{document}
不带该版本的版本l3regex
可能更容易被依赖旧版 TeX 发行版的出版商所接受(由 Bruno Le Floch 提供):
\ExplSyntaxOn
\NewDocumentCommand{\evaluateorprint}{m}
{
\group_begin:
\cs_set_eq:NN \__sungmin_eval:n \int_eval:n
\tl_map_inline:nn { #1 }
{
\tl_if_in:nnF { 0123456789+-*() } { ##1 }
{
\cs_set_eq:NN \__sungmin_eval:n \use:n
}
}
\__sungmin_eval:n { #1 }
\group_end:
}
\ExplSyntaxOff
函数\__sungmin_eval:n
暂时设置为\int_eval:n
;然后逐个标记地扫描参数;如果发现数值表达式中不合法的内容,\__sungmin_eval:n
则将其更改为\use:n
仅输出参数而不进行任何处理。
答案2
我知道我的解决方案乍一看并不优雅(如 egregs),但它只使用 TeX 基元,因此适用于所有人。而且它只使用了 27 行代码(另一方面,加载\usepackage{xparse,l3regex}
了数万行代码)。
% \replacestring + \addto from OPmac:
\newcount\tmpnum
\bgroup \catcode`!=3 \catcode`?=3
\gdef\replacestrings#1#2{%
\long\def\tmp##1#1##2!{\ifx!##2!\addto\tmpb{##1}\else\addto\tmpb{##1#2}\tmp##2!\fi}%
\expandafter\def\expandafter\tmpb\expandafter{\expandafter}\expandafter\tmp\tmpb?#1!%
\def\tmp##1?{\def\tmpb{##1}}\expandafter\tmp\tmpb
}
\egroup
\long\def\addto#1#2{\expandafter\def\expandafter#1\expandafter{#1#2}}
% \evalindex{text} evaluates text if it is sum of numbers; else the text is printed
\def\evalindex#1{\def\tmpa{#1}\evalindexA#1\evalindexB.\evalindexB\evalindexA}
\def\evalindexA#1#2\evalindexA{%
\ifx-#1\def\tmpb{#1#2}\else\def\tmpb{+#1#2}\fi
\replacestrings+{\evalindexB+}\replacestrings-{\evalindexB-}%
\tmpnum=0 \tmpb
}
\def\evalindexB#1#2\evalindexB{\ifx#1.\the\tmpnum
\else \setbox0=\hbox{$\tmpnum=0#2$}%
\ifdim\wd0=0pt \advance\tmpnum by#1#2 \else \evalindexC \fi
\expandafter\evalindexB \fi
}
\def\evalindexC#1.\evalindexB{\fi\fi \tmpa}
\def\foo#1#2{X_{\evalindex{#1+#2+1}}}
% tests:
$\evalindex{i+j+1} \quad \evalindex{1+2\alpha+3}\quad \evalindex{-1+20-3}$
% i+j+1 1+2\alpha+3 16
$\foo ij \quad \foo\alpha\beta \quad \foo 23$
% X_{i+j+1} X_{\alpha+\beta+1} X_6
\end
这给出的结果与 egregs 解决方案相同。但我知道,你不会投票赞成。玩我的玩具——原始特征只是我的爱好。
答案3
此类命令可以通过包轻松完成calculator
,尽管也可以使用计数器方法,只要使用整数索引即可。
在这种情况下,应该将的两个参数\foo
相加并增加 1,这可以通过使用
\ADD{#1}{#2}{\resulta}%
\ADD{\resultb}{1}{\resultb}%
通过使用计数器,例如两种可能性——应用\setcounter
和\addtocounter
或\numexpr
方法。\Foo
并\FooAgain
证明这一点。
\documentclass{scrbook}
\usepackage{calculator}
\usepackage{forloop}
\newcounter{acounter}
\newcounter{bcounter}
\newcommand{\foo}[2]{%
\begingroup
\def\resultA{}%
\def\resultB{}%
\ADD{#1}{#2}{\resultA}%
\ADD{\resultA}{1}{\resultB}%
X_{\resultB}
\endgroup%
}%
\newcounter{mydummycounter}
\newcommand{\Foo}[2]{%
\setcounter{mydummycounter}{#1}%
\addtocounter{mydummycounter}{#2}%
\addtocounter{mydummycounter}{1}%
Y_{\arabic{mydummycounter}}%
}%
\newcommand{\FooAgain}[2]{%
\setcounter{mydummycounter}{\numexpr #1 + #2 +1}%
Z_{\arabic{mydummycounter}}%
}%
\begin{document}
Testing:
\[ \foo{2}{3} \]
Some playing around:
\LARGE \bfseries
\forloop{acounter}{1}{\value{acounter} < 11}{%
\forloop{bcounter}{1}{\value{bcounter} < \value{acounter}}{%
\( \foo{\number\value{acounter}}{\number\value{bcounter}} \)
}
}
\end{document}