我写了以下函数。
f :: Integer -> Integer
f x = if (odd x) then 0 else (floor . logBase 2) x
但是发生以下编译时错误:
F.hs:2:31: 使用
floor' Possible fix: add an instance declaration for (RealFrac Integer) In the first argument of
(。)'时没有(RealFrac Integer)的实例,即“floor” 在表达式:floor。 logBase 2 在表达式中:( floor.logBase 2)xF.hs:2:39: 使用
logBase' Possible fix: add an instance declaration for (Floating Integer) In the second argument of
(。)',即`logBase 2'时,没有(浮动整数)的实例 在表达式:floor。 logBase 2 在表达式中:( floor.logBase 2)x失败,模块加载:无。
如何正确编写上述功能?
答案 0 :(得分:9)
这将是一个很长的时间,因为我不仅仅是为您提供有效的代码,而是深入解释这个问题,以便您更好地理解GHC的类型错误。
正如已经简要回答的那样(并且当类型错误尽力告诉你,虽然它当然不够清楚),为了使用logBase x y
,两个参数x
和{{ 1}}必须都是“浮点”类型类的实例。
特别是,y
是logBase
类型类的方法(来自Prelude's documentation):
Floating
我们也发现,也来自前奏曲:
class Fractional a => Floating a where Source
logBase :: a -> a -> a
也就是说,为了使用函数class (Real a, Fractional a) => RealFrac a where Source
floor :: Integral b => a -> b
,我们需要两个参数:(floor . logBase)
(因为Fractional
需要这个),logBase
(自{{{ 1}}需要两个)。这两者的合并被定义为Real
,这正是GHC抱怨你未能提供它(在你的函数的类型声明中)。
为什么抱怨?在Prelude中,我们为floor
找到了以下RealFrac
声明。请注意,“instance
”缺失:
RealFrac
Haskell的工作方式是,如果你给它一个整数文字(没有小数点的连续数字),它将假设它属于RealFrac Integer
类型类(并将试图弄清楚是否要制作它是隐式RealFrac Double
RealFrac Float
RealFrac CDouble
RealFrac CFloat
Integral a => RealFrac (Ratio a)
HasResolution a => RealFrac (Fixed a)
或Integral
,但永远不会隐式地将整数文字提升为其中一个Integer
类(包括Int
) 。由于没有“Fractional
”行,这意味着您不能指望Haskell编译代码。
你告诉Haskell你将通过你的显式类型声明给它RealFrac
个实例(这是为什么这些通常是一个好主意的原因之一 - Haskell会悄悄地接受你的函数声明,否则,在使用它的客户端函数中抛出编译错误):
RealFrac Integer
解决方案是使用以下函数(将Integral
转换为任何兼容的f :: Integer -> Integer
类型)来提升整数:
Integral
Num
执行相反方向的转换(从fromIntegral :: (Integral a, Num b) => a -> b
到Floor
),如其类型所示。
总之,您只需说出
即可Fractional
请注意Integral
调用,使参数类型与编译器期望的内容兼容,以及使用f :: Integer -> Integer
f x = if (odd x) then 0 else (floor . logBase 2.0 . fromIntegral) x
(fromIntegral
字面值)作为基础。< / p>
答案 1 :(得分:1)
请注意logBase
需要转换为Floating
类型,这可能会导致错误的结果。
f :: Integer -> Integer
f x = if (odd x) then 0 else (floor . logBase 2.0 . fromIntegral) x
λ> f (2^99999)
179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137216
这是因为(2^99999 :: Double) = Infinity
,floor Infinity
显然评估为......令人惊讶的事情。
integer-logarithms包提供了integerLog2
功能,效果更好:
λ> import Math.NumberTheory.Logarithms
λ> integerLog2 (2^99999)
99999
it :: Int
integer-logarithms
中的函数只是integer-gmp的一个薄包装,所以你也可以直接使用它:
λ> :set -XMagicHash
λ> import GHC.Exts
λ> import GHC.Integer.Logarithms
λ> I# (integerLog2# (2^99999))
99999
it :: Int
请注意,即使结果不是2的幂,这些函数也会返回一个值:
λ> integerLog2 1023
9