Haskell 中的求幂

发布于 2024-11-16 06:33:09 字数 166 浏览 3 评论 0原文

有人可以告诉我为什么 Haskell Prelude 定义了两个单独的求幂函数(即 ^**)?我认为类型系统应该消除这种重复。

Prelude> 2^2
4
Prelude> 4**0.5
2.0

Can someone tell me why the Haskell Prelude defines two separate functions for exponentiation (i.e. ^ and **)? I thought the type system was supposed to eliminate this kind of duplication.

Prelude> 2^2
4
Prelude> 4**0.5
2.0

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

月亮邮递员 2024-11-23 06:33:09

实际上存在三个求幂运算符:(^)(^^)(**)^ 是非负整数幂,^^ 是整数幂,** 是浮点幂:

(^) :: (Num a, Integral b) => a -> b -> a
(^^) :: (Fractional a, Integral b) => a -> b -> a
(**) :: Floating a => a -> a -> a

原因是类型安全:数值运算的结果通常与输入参数具有相同的类型。但您无法将 Int 进行浮点幂并获得 Int 类型的结果。因此类型系统会阻止您执行此操作:(1::Int) ** 0.5 会产生类型错误。 (1::Int) ^^ (-1) 也是如此。

另一种说法是:Num 类型在 ^ 下封闭(它们不需要具有乘法逆元),Fractional 类型在 ^ 下封闭^^Floating 类型在 ** 下封闭。由于 Int 没有 Fractional 实例,因此您无法将其计算为负幂。

理想情况下,^ 的第二个参数静态地约束为非负数(目前,1 ^ (-2) 会引发运行时异常)。但 Prelude 中没有自然数的类型。

There are actually three exponentiation operators: (^), (^^) and (**). ^ is non-negative integral exponentiation, ^^ is integer exponentiation, and ** is floating-point exponentiation:

(^) :: (Num a, Integral b) => a -> b -> a
(^^) :: (Fractional a, Integral b) => a -> b -> a
(**) :: Floating a => a -> a -> a

The reason is type safety: results of numerical operations generally have the same type as the input argument(s). But you can't raise an Int to a floating-point power and get a result of type Int. And so the type system prevents you from doing this: (1::Int) ** 0.5 produces a type error. The same goes for (1::Int) ^^ (-1).

Another way to put this: Num types are closed under ^ (they are not required to have a multiplicative inverse), Fractional types are closed under ^^, Floating types are closed under **. Since there is no Fractional instance for Int, you can't raise it to a negative power.

Ideally, the second argument of ^ would be statically constrained to be non-negative (currently, 1 ^ (-2) throws a run-time exception). But there is no type for natural numbers in the Prelude.

命硬 2024-11-23 06:33:09

Haskell 的类型系统不够强大,无法将三个求幂运算符表示为一个。你真正想要的是这样的:

class Exp a b where (^) :: a -> b -> a
instance (Num a,        Integral b) => Exp a b where ... -- current ^
instance (Fractional a, Integral b) => Exp a b where ... -- current ^^
instance (Floating a,   Floating b) => Exp a b where ... -- current **

即使你打开多参数类型类扩展,这也不会真正起作用,因为实例选择需要比 Haskell 目前允许的更聪明。

Haskell's type system isn't powerful enough to express the three exponentiation operators as one. What you'd really want is something like this:

class Exp a b where (^) :: a -> b -> a
instance (Num a,        Integral b) => Exp a b where ... -- current ^
instance (Fractional a, Integral b) => Exp a b where ... -- current ^^
instance (Floating a,   Floating b) => Exp a b where ... -- current **

This doesn't really work even if you turn on the multi-parameter type class extension, because the instance selection needs to be more clever than Haskell currently allows.

无远思近则忧 2024-11-23 06:33:09

它没有定义两个运算符——而是定义了三个!报告中:

共有三种二参数求幂运算:(^) 计算任意数字的非负整数次方,(^^) 计算小数的任意整数次方、和 (**) 采用两个浮点参数。对于任何 xx^0x^^0 的值为 1,包括零; 0**y 未定义。

这意味着存在三种不同的算法,其中两种给出精确结果(^^^),而 ** 给出近似结果。通过选择要使用的运算符,您可以选择要调用的算法。

It doesn't define two operators -- it defines three! From the Report:

There are three two-argument exponentiation operations: (^) raises any number to a nonnegative integer power, (^^) raises a fractional number to any integer power, and (**) takes two floating-point arguments. The value of x^0 or x^^0 is 1 for any x, including zero; 0**y is undefined.

This means there are three different algorithms, two of which give exact results (^ and ^^), while ** gives approximate results. By choosing which operator to use, you choose which algorithm to invoke.

冬天的雪花 2024-11-23 06:33:09

^ 要求其第二个参数是 Integral。如果我没记错的话,如果您知道正在使用积分指数,那么实现会更有效。另外,如果您想要类似 2 ^ (1.234) 的值,即使您的底数是整数 2,您的结果显然也是小数。您有更多选项,以便可以更严格地控​​制进出指数函数的类型。

Haskell 的类型系统与其他类型系统(例如 C、Python 或 Lisp)的目标不同。鸭子类型(几乎)与 Haskell 思维方式相反。

^ requires its second argument to be an Integral. If I'm not mistaken, the implementation can be more efficient if you know you are working with an integral exponent. Also, if you want something like 2 ^ (1.234), even though your base is an integral, 2, your result will obviously be fractional. You have more options so that you can have more tight control over what types are going in and out of your exponentiation function.

Haskell's type system does not have the same goal as other type systems, such as C's, Python's, or Lisp's. Duck typing is (nearly) the opposite of the Haskell mindset.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文