将数字提高到分数(Data.Ratio)次方?

Raising a number to a fractional(Data.Ratio) power?

2^(2%1) 这样的表达式不会在 GHCi 中进行类型检查,并且错误消息是含糊不清的。为什么这不起作用,我需要更改什么?

我无法转换为另一种类型,我想将其用于 27^(1%3).

等表达式

Haskell 有 三个 幂运算符:

  1. (^) :: (Num a, Integral b) => a -> b -> a

    这会使用 正积分 指数引发 任何 类型的数字。

    您在键入 2^(2%3) 时收到类似 Could not deduce (Integral (Ratio a0)) arising from a use of ‘^’ 的错误,因为 Data.Ratio 而不是 Integral 的实例。 GHC 看到 ^ 想要一个 Integral 并注意到 Data.Ratio 不能在这种情况下使用。

  2. (^^) :: (Fractional a, Integral b) => a -> b -> a

    此运算符允许 负积分 指数。请记住 x^(-n) == 1/(x^n)。这就是为什么它需要 Fractional.

    请注意,指数必须仍然是整数。 2^^(1%2) 不是 是一个 Fractional 数字。

  3. (**) :: Floating a => a -> a -> a

    这是 "catch all" 运算符。它可以将小数提高到分数次幂。然而,这使用浮点数,而不是精确的有理数。

由于我们不能 表示所有实数,他们决定在需要不精确运算时只依赖浮点数。

因此您应该使用类型转换来执行该操作。 A 可能的实现可能是:

realToFrac $ 27**(realToFrac $ 2%3) :: Rational

或者您可以定义一个新的运算符:

(*^*) :: (RealFrac a, RealFrac b) => a -> b -> a
x *^* y = realToFrac $ realToFrac x ** realToFrac y

这将允许你写:

27 *^* (2%3)

我用了两个 * 来提醒实现中使用的 ** 并且我添加了一个 ^ 来引用类型前两个运算符...不确定这是否有意义,或者 ^**^^* 会更好。

然而,简单地使用 Doubles 可能会更好。这实际上取决于数字代表什么以及您用它们做什么。

解决“为什么这不起作用”——Haskell 的多重指数运算符实际上非常接近地反映了数学家定义指数的方式!

  • 从头开始,您只知道(直接根据定义)对于自然 n,表达式 xn表示1·x·x·x…,n 使用 x。这正是 ^ 实现的,它显然适用于任何数字类型(因为乘法总是被定义)。
  • 通过跨越零的“反向延续”,定义 x-n 是有意义的作为1/x·x·x。这仅在您具有乘法逆元(即分数)时才有效。嗯,Fractional,你懂的!这就是 ^^ 所做的。
  • 要定义 分数指数,您可以从考虑根开始。这些基本上就是您要的。然而,根实际上在数学上非常复杂。它们并不是真正独一无二的。这样的根的结果类型将是代表 algebraic numbers 的东西。对于大多数用户来说,没有太多理由专门处理这些数字,因此 Haskell 跳过此部分。
    现在你说,好吧,但我们当然可以为特定数字定义根。 27 的三次方恰好是一个整数,但对于大多数其他数字而言,情况并非如此。不明确或部分功能是邪恶的;因此,我们需要像 Num a => a -> Rational -> Maybe a 这样的签名。但是结果 通常 Nothing,所以老实说这将是一个非常无用的函数。喜欢就自己实现吧。
  • 如果你直接更进一步,它会更有用:从代数切换到微积分,数学家可以访问 exponential 和对数函数,这些为你提供了所有幂表达式的覆盖面,凭借平等
    xy = (exp(ln x))y = exp(y · ln x)。
    只要 x 为正 ,此方法就有效。对于指数的定义,在数学上你需要 limits. These exist in complete spaces,即“没有孔的空间”。根据定义,实数是完整的,但有理数不是!举个例子:Rational 是“无限准确”的,即每个值实际上都是一个精确的数字。但是,实际上您只能使用有限数量的此类有理数。
    OTOH,浮点类型是设计使然不准确This often leads to confusion,但在数学上它实际上可以很好地解释:每个 Double 值代表一个完整的 interval 实数,如果你取所有这些的单位您得到的值不仅仅是一组离散点,而是 整条实线.
    这就是 Floating class 及其实例 Double 表示完整空格的方式,以及为什么此 class 具有方法 (**) :: Floating a => a -> a -> a.

complex numbers中你甚至可以定义负数的对数,尽管它再次导致唯一性问题。