计算器 abacus 是一个小巧却功能齐备的计算器,支持四则混合运算(包括逻辑运算),支持大量的数学函数,支持变量参与运算,支持自定义函数以扩充功能。目前版本是 2,地址:http://www.oschina.net/code/snippet_736932_13725。本文就自定义函数作一介绍。
用户可以将含有参数的表达式定义为一个新函数,以实现含参表达式的复用,对于一元二次方程求根,可以定义函数
SolveEqution1x2p(a, b, c) = (- b + sqrt(b ^ 2 - 4 * a * c)) / (2 * a)
那么没有参数的表达式就不能定义成函数吗?照样可以,只要你喜欢,假使你不喜欢使用符号常量,你仍然可以通过定义函数来使用圆周率:Pi() = 3.141593,然后在需要圆周率的地方调用它就行了。进一步,可以在已定义函数的基础上定义新的函数,比如你定义了圆的面积函数(下式中pi 是符号常量,圆周率):
AreaCircle(r) = pi * r * r
就可以继续定义圆环的面积
AreaRing(r1, r2) = AreaCircle(r1)- AreaCircle(r2)
如何,很刺激吧?我们来看一个更有趣的例子,先介绍一下程序内置的 if 条件函数
if(x, a, b)
这个函数有三个参数,当第一个参数 x 不为零时函数返回第二个参数 a,当第一个参数为零时返回第三个参数 b,从而实现选择的功能,实际上由于程序提供了关系运算符,我们完全可以自己来定义这个函数(下式中双等号 == 表示相等,单等号是用作赋值符号的)
if(x, a, b) = a + (b - a) * (x == 0)
有了这个函数,我们甚至可以写出这样的阶乘函数
factorial(n) = if(n == 0, 1, n * factorial(n - 1))
意思是
factorial(0) = 1, factorial(n) = n * factorial(n - 1)
这个例子是如此的特殊,在函数的定义体中居然出现了它自己,这叫做递归,实现起来并没有技术障碍。同样的方式,我们可以这样计算余弦
cos(x) = if(abs(x) < 0.000001, 1, 2 * cos(x / 2) ^ 2 - 1)
这个计算是近似的,当 x 充分接近 0 的时候,取 1 为它的余弦值,否则先计算它的一半的余弦,再按倍角公式计算它自己的余弦。这技术的确很诱人,但是这会导致一个问题,就是函数之间的依赖关系,这在删除函数的时候会导致问题,如果我把圆的面积函数给删除了,那前面那个圆环的面积函数还可以使用吗?有一种思路,就是在处理用户自定义函数时采用一个原则:如果在定义函数时使用了已经定义的其他函数,内部最终必须转化为只依赖于内置函数,这样,这个圆环的面积函数在经过处理后,内部事实上是
pi * r1 * r1 - pi * r2 * r2
不过这个解决方法也是有缺陷的,首先是不支持递归,比如上面的阶乘函数将变得不可能,因为会陷入无穷替换,更严重的问题是它会导致错误信息变得不可理解,比如我们不喜欢符号常量,我们自己写一个函数来计算圆周率:Pi(),而且这个函数是有可能会计算失败的,然后定义圆的面积为
AreaCircle(r) = Pi() * r * r
再定义圆环的面积为
AreaRing(r1, r2)= AreaCircle(r1) - AreaCircle(r2)
那么按照前面所述的原则,在定义完成后计算器内部实际保存的是 Pi() * r1 * r1 - Pi() * r2 * r2,然后我们调用这个圆环的面积函数,可能会得出一个对函数 Pi() 的调用失败的提示,咋一看,不对呀,我们这里并没有调用函数 Pi() 呀,于是就丈二和尚摸不着头脑了。鉴于这两个原因,程序中将不采用上面这种原则,而采用构造依赖关系表来解决这个问题,每个函数在定义时将会构造一个它所依赖的函数列表(如果有递归,则对自己的依赖不会记入此列表),在删除函数的时候,会先删除依赖它的函数,包括间接依赖它的函数,然后再删除它自己。来源:oschina
链接:https://my.oschina.net/u/736932/blog/137900