在Haskell中求幂
有人可以告诉我为什么Haskell Prelude为取幂(即^
和**
)定义了两个单独的函数吗? 我认为types系统应该消除这种重复。
Prelude> 2^2 4 Prelude> 4**0.5 2.0
实际上有三个指数运算符: (^)
, (^^)
和(**)
。 ^
是非负整数指数, ^^
是整数指数, **
是浮点指数:
(^) :: (Num a, Integral b) => a -> b -> a (^^) :: (Fractional a, Integral b) => a -> b -> a (**) :: Floating a => a -> a -> a
原因是types安全:数值运算的结果通常与input参数具有相同的types。 但是你不能把一个Int
提升到一个浮点数,并得到Int
types的结果。 所以types系统阻止你这样做: (1::Int) ** 0.5
产生一个types错误。 (1::Int) ^^ (-1)
。
另一种方法是把这个: Num
types在^
下closures(它们不需要有乘法逆), Fractional
types在^^
下closures, Floating
types在**
下closures。 由于没有Int
Fractional
实例,所以不能将其提升为负值。
理想情况下, ^
的第二个参数将被静态约束为非负数(目前, 1 ^ (-2)
抛出运行时exception)。 但是Prelude
没有自然数的types。
Haskell的types系统不足以将三个指数运算符表示为一个。 你真正想要的是这样的:
class Exp ab where (^) :: a -> b -> a instance (Num a, Integral b) => Exp ab where ... -- current ^ instance (Fractional a, Integral b) => Exp ab where ... -- current ^^ instance (Floating a, Floating b) => Exp ab where ... -- current **
即使打开多参数types类扩展,这也不会起作用,因为实例select需要比Haskell当前允许的更为巧妙。
它没有定义两个运算符 – 它定义了三个! 从报告:
有三个双参数指数运算:(
^
)将任意数字提升为一个非负整数幂,(^^
)将一个分数数字提升为任意整数幂,(**
)取两个浮点数。 对于任何x
,x^0
或x^^0
都是1,包括零;0**y
是未定义的。
这意味着有三种不同的algorithm,其中两种给出精确的结果( ^
和^^
),而**
给出近似的结果。 通过select要使用的运算符,您可以select要调用的algorithm。
^
要求其第二个参数是一个Integral
。 如果我没有弄错,如果你知道你正在使用一个整数指数,那么这个实现会更有效率。 另外,如果你想要2 ^ (1.234)
这样的东西,即使你的基数是一个积分,2,你的结果显然是小数。 你有更多的select,所以你可以更严格地控制什么types进出指数函数。
Haskell的types系统与其他types的系统(如C,Python或Lisp)没有相同的目标。 鸭子打字(几乎)和哈斯克尔的思维方式相反。