将数字提高到小数(Data.Ratio)?

Lud*_*wik 2 haskell types exponentiation ghci

2^(2%1)GHCi中没有类型检查的表达式,错误消息是神秘的.为什么这不起作用,我需要改变什么?

我不能转换为另一种类型,我想要这样的表达式27^(1%3).

Bak*_*riu 6

Haskell有三个电源运营商:

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

    这使用正整数指数提出任何类型的数字.

    Could not deduce (Integral (Ratio a0)) arising from a use of ‘^’在输入时遇到错误,2^(2%3)因为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

    这是"全部捕获"操作员.它可以将分数提升到分数幂.然而,这使用浮点数,而不是精确的有理数.

由于我们无法表示所有实数,因此当您需要不精确的操作时,他们决定简单地依赖浮点数.

因此,您应该使用类型转换来执行该操作.一个可能的实现可能是:

realToFrac $ 27**(realToFrac $ 2%3) :: Rational
Run Code Online (Sandbox Code Playgroud)

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

(*^*) :: (RealFrac a, RealFrac b) => a -> b -> a
x *^* y = realToFrac $ realToFrac x ** realToFrac y
Run Code Online (Sandbox Code Playgroud)

这将允许你写:

27 *^* (2%3)
Run Code Online (Sandbox Code Playgroud)

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

然而,简单地使用Doubles 可能更好.这实际上取决于数字代表什么以及你在做什么.


lef*_*out 5

要说"为什么这不起作用" - Haskell的多指数运算符实际上反映了数学家定义取幂的方式!

  • 从头开始,你只知道(直接定义)对于自然n,表达式x n表示1· x · x · x ...,n使用x.这正是^实现的,它显然适用于任何数字类型(因为总是定义乘法).
  • 通过零的"反向延续",将x - n定义为1/x · x · x ...是有意义的.这仅在您有乘法反转时才有效,即分数.嗯Fractional,你知道!这是做什么的^^.
  • 要定义小数指数,您可以从考虑根开始.这些基本上就是你所要求的.然而,事实上,根在数学上非常复杂.它们并不是真正独特的.这种根的结果类型将代表代数数字.对于大多数用户来说,没有太多理由对这些数字进行专门的处理,因此Haskell会跳过这个.
    现在你说,好吧,但我们当然可以为特定数字定义根.27的第三个根恰好是一个整数,但对于大多数其他数字,这不是真的.不明确或部分功能是邪恶的; 因此我们需要签名Num a => a -> Rational -> Maybe a.但结果通常会Nothing,老实说,这将是一个非常无用的功能.如果你愿意,可以自己实施.
  • 如果你直接向前迈进一步更有用:从代数转换到微积分,数学家可以访问指数函数和对数函数,并且这些函数可以为所有幂函数表达式提供覆盖,由于等式
                x y =( exp(ln x))y = exp(y ·ln x).
    只要x为正†,这就有效.对于指数的定义,在数学上你需要限制.它们存在于完整的空间中,即"没有空洞的空间".根据定义,实数是完整的,但理性不是!手动原因:Rational是"无限准确",即每个值实际上是一个确切的数字.但是,实际上你只能使用有限数量的这种有理数.
    OTOH,浮点类型的设计并不精确.这通常会导致混淆,但在数学上它实际上可以很好地解释:每个Double值代表实数的整个区间,如果你取所有这些值的单位,你不仅得到一组离散点,而且整个实线.
    这就是Floating具有其实例的类如何Double表示完整空间,以及为什么这个类具有该方法(**) :: Floating a => a -> a -> a.

复数中,您甚至可以定义负数的对数,但它会再次导致唯一性问题.