【问题标题】:Raising a number to a fractional(Data.Ratio) power?将数字提高到分数(Data.Ratio)幂?
【发布时间】:2015-11-17 11:25:09
【问题描述】:

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

我无法转换为另一种类型,我想要像 27^(1%3) 这样的表达式。

【问题讨论】:

  • 你希望结果是什么类型的?
  • @Ludwik 有一个Integral a => Fractional (Ratio a) 实例,但没有Ratio aIntegral 实例。
  • AFAIK 在 Haskell 中有三个电源运算符:(^) :: (Num a, Integral b) => a -> b -> a(^^) :: (Fractional a, Integral b) => a -> b -> a(**) :: (Floating a) => a -> a -> a。所以指数要么是Integral要么是Floating,但没有可用的Fractional指数......你应该使用27**(realToFrac $ 2%3)
  • 我第二个拉姆达仙子;你希望结果是什么type?你想要Ratio 吗? (2^(1/2) 不是有理数。)你想要最接近的整数吗?你想要Double 吗?你想要什么type

标签: haskell types ghci exponentiation


【解决方案1】:

为了解决“为什么这不起作用”——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。这些存在于complete spaces,即“没有孔的空间”。根据定义,实数是完整的,但有理数不是!手动说明原因:Rational 是“无限准确的”,即每个值实际上都是一个精确的数字。但是,实际上您只能使用有限数量的此类有理数。
    OTOH,浮点类型的设计不精确This often leads to confusion,但在数学上它实际上可以很好地解释:每个Double 值代表一个完整的实数间隔,如果你采用所有这些值的单位,你得到的不仅仅是一个集合离散点,但整个实线
    这就是 Floating 类及其 Double 实例表示完整空间的方式,以及该类具有 (**) :: Floating a => a -> a -> a 方法的原因。

complex numbers 中,您甚至可以定义负数的对数,但这又会导致唯一性问题。

【讨论】:

  • 感谢您对数论的深入了解,真的很有见地。
【解决方案2】:

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

    这是“全能”运算符。它可以将分数提高到分数幂。然而,这使用浮点数,而不是精确的有理数。

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

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

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

或者你可以定义一个新的操作符:

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

这会让你写:

27 *^* (2%3)

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

不过,最好直接使用Doubles。这实际上取决于数字代表什么以及您使用它们做什么。

【讨论】:

    猜你喜欢
    • 2021-09-09
    • 1970-01-01
    • 2018-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-25
    相关资源
    最近更新 更多