我不明白Haskell中的数字转换

时间:2011-08-09 19:54:14

标签: haskell sqrt

这是我正在尝试做的事情:

isPrime :: Int -> Bool
isPrime x = all (\y -> x `mod` y /= 0) [3, 5..floor(sqrt x)]

(我知道我不会检查两个分区 - 请忽略它。) 这是我得到的:

No instance for (Floating Int)
  arising from a use of `sqrt'
Possible fix: add an instance declaration for (Floating Int)
In the first argument of `floor', namely `(sqrt x)'
In the expression: floor (sqrt x)
In the second argument of `all', namely `[3, 5 .. floor (sqrt x)]'

我花了好几个小时尝试了我能想到的一切,使用sqrt的一些变体来制作这个列表,包括像

这样的废话
intSqrt :: Int -> Int
intSqrt x = floor (sqrt (x + 0.0))

似乎(sqrt 500)工作正常但是(sqrt x)坚持x是浮动(为什么?),并且没有找到将Int转换为实数的函数(为什么?)。

我不想要一种测试素性的方法,我想了解如何解决这个问题。为什么这么难?

2 个答案:

答案 0 :(得分:21)

与大多数其他语言不同,Haskell严格区分整数和浮点类型,并且不会隐式地将一个转换为另一个。有关如何明确执行转换,请参阅here。甚至有sqrt例子: - )

潜在的原因是隐式转换和Haskel(相当复杂但非常酷)的类系统的组合会使类型重建变得非常困难 - 可能它会超出它可以由机器完成的程度。所有。语言设计者认为获取算术类型类是值得明确指定转换的成本。

答案 1 :(得分:19)

您的问题是,虽然您尝试以各种方式修复它,但您还没有尝试过x,这正是您的问题所在。我们来看看sqrt

的类型
Prelude> :t sqrt
sqrt :: (Floating a) => a -> a

另一方面,xInt,如果我们向GHCi询问有关Floating的信息,它会告诉我们:

Prelude> :info Floating
class (Fractional a) => Floating a where
  pi :: a
  <...snip...>
  acosh :: a -> a
    -- Defined in GHC.Float
instance Floating Float -- Defined in GHC.Float
instance Floating Double -- Defined in GHC.Float

因此,Floating的唯一类型是FloatDouble。我们需要一种方法将Int转换为Double,就像floor :: (RealFrac a, Integral b) => a -> b转向另一个方向一样。每当你遇到这样的类型问题时,你可以问Hoogle,这是一个搜索类型的Haskell搜索引擎。不幸的是,如果你搜索Int -> Double,你会得到糟糕的结果。但是,如果我们放松我们正在寻找的东西呢?如果我们搜索Integer -> Double,我们会发现有一个函数fromInteger :: Num a => Integer -> a,这几乎就是您想要的。如果我们将我们的类型一直放到(Integral a, Num b) => a -> b,您会发现有一个函数fromIntegral :: (Integral a, Num b) => a -> b

因此,要计算整数的平方根,请使用floor . sqrt $ fromIntegral x或使用

isqrt :: Integral i => i -> i
isqrt = floor . sqrt . fromIntegral

您正在考虑sqrt输出正确方向的问题;它返回一个浮点数,但你想要一个整数。但是,在Haskell中,没有子类型或隐式强制转换的概念,因此您还需要将输入更改为sqrt

解决您的其他一些问题:

intSqrt :: Int -> Int
intSqrt x = floor (sqrt (x + 0.0))

你称之为“废话”,所以很明显你不指望它能起作用,但为什么不呢?好吧,问题是(+)的类型为Num a => a -> a -> a - 你只能添加两个相同类型的东西。这通常是好的,因为这意味着你不能将复数加到5×5实矩阵中;但是,由于0.0必须是Fractional的实例,因此您无法将其添加到x :: Int

  

似乎(sqrt 500)工作正常......

这是有效的,因为500的类型不是您所期望的。让我们问一下我们可信赖的同伴GHCi:

Prelude> :t 500
500 :: (Num t) => t

实际上,所有整数文字都有这种类型;它们可以是任何类型的数字,这是有效的,因为Num类包含函数fromInteger :: Integer -> a。因此,当您撰写sqrt 500时,GHC意识到500需要满足500 :: (Num t, Floating t) => t(并且它会隐含地选择Double这样的数字类型,感谢defaulting rules )。同样,由于0.0的{​​{1}}函数,上面的Fractional t => t类型为Fractional

  

...但是(sqrt x)坚持认为x是浮动...

见上文,我们查看fromRational :: Rational -> a的类型。

  

...并且我找不到将Int转换为真实的函数....

嗯,你现在有一个:sqrt。我不知道为什么你找不到它;显然Hoogle的结果比我预期的要糟糕得多,这要归功于函数的泛型类型。

  

为什么这么难?

我希望现在已经没有了fromIntegral