Haskell 用于计算整数的“log”的函数

Haskell 用于计算整数的“log”的函数,haskell,Haskell,我编写了以下函数 f :: Integer -> Integer f x = if (odd x) then 0 else (floor . logBase 2) x 但出现以下编译时错误: F.hs:2:31: 没有因使用floor'而产生的(RealFrac Integer)实例 可能的修复方法:为(RealFrac Integer)添加实例声明 在(.)的第一个参数中,即'floor' 在表达中:地板。后勤基地2 在表达式中:(floor.logBase 2)x F.hs:2:39

我编写了以下函数

f :: Integer -> Integer
f x = if (odd x) then 0 else (floor . logBase 2) x
但出现以下编译时错误:

F.hs:2:31: 没有因使用
floor'而产生的(RealFrac Integer)实例
可能的修复方法:为(RealFrac Integer)添加实例声明
在
(.)的第一个参数中,即'floor' 在表达中:地板。后勤基地2 在表达式中:(floor.logBase 2)x

F.hs:2:39: 没有因使用
logBase'而产生的(浮点整数)实例
可能的修复方法:添加(浮点整数)的实例声明
在
(.)”的第二个参数中,即“logBase 2” 在表达中:地板。后勤基地2 在表达式中:(floor.logBase 2)x失败,加载的模块:无

如何正确地编写上述函数?

这将有点长,因为我不仅要给您提供有效的代码,还要深入解释问题,以便您更好地理解GHC的类型错误

如前所述(类型错误尽其所能告诉您,尽管它肯定不够清楚),为了使用
logBase x y
,两个参数
x
y
必须都是“浮点”类型类的实例

特别是,
logBase
是一种
浮动
类型类的方法(来自:

我们还从序曲中发现:

class (Real a, Fractional a) => RealFrac a where Source
    floor :: Integral b => a -> b
也就是说,为了使用函数
(floor.logBase)
,我们需要两个参数,分别是
分数
(因为
logBase
需要这个参数)和
实数
(因为
floor
两者都需要)。这两者的合并定义为
RealFrac
,GHC正是抱怨您未能提供它(在函数的类型声明中)

为什么它在抱怨?从前奏曲中,我们可以找到
RealFrac
的以下
实例
声明。请注意,缺少“
RealFrac Integer
”:

RealFrac Double  
RealFrac Float   
RealFrac CDouble     
RealFrac CFloat  
Integral a => RealFrac (Ratio a)     
HasResolution a => RealFrac (Fixed a)   
Haskell的工作方式是,如果您给它一个整数文本(连续的数字没有小数点),它将假定它属于
Integral
typeclass(并将尝试确定是将其隐式设置为
integer
还是
Int
),但它将从不隐式地将整数文本提升为
分数
类之一(包括
RealFrac
)。由于没有“
RealFrac Integer
”行,这意味着您不能指望Haskell编译代码

您正在告诉Haskell,您将通过显式类型声明为它提供
Integral
实例(这就是为什么这些通常是一个好主意的原因之一——否则Haskell会悄悄地接受您的函数声明,只会在使用它的客户机函数中抛出编译错误):

解决方案是使用以下函数提升整数(将
Integral
s转换为任何兼容的
Num
ber类型):

Floor
按相反方向执行转换(从
分数
积分
),如其类型所示

总之你只需要说

f :: Integer -> Integer
f x = if (odd x) then 0 else (floor . logBase 2.0 . fromIntegral) x

请注意
fromIntegral
调用,以使参数类型与编译器期望的类型兼容,以及使用
2.0
(a
fractal
literal)作为基。

注意
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)=无限
,而
楼层无限
的计算结果显然是。。。令人惊讶的事情

该软件包提供了一个更好的功能:

λ> import Math.NumberTheory.Logarithms

λ> integerLog2 (2^99999)
    99999
it :: Int
整数对数
中的函数只是一个简单的包装,因此您也可以直接使用它:

λ> :set -XMagicHash

λ> import GHC.Exts

λ> import GHC.Integer.Logarithms

λ> I# (integerLog2# (2^99999))
    99999
it :: Int
请注意,即使结果不是二的幂,这些函数也会返回一个值:

λ> integerLog2 1023
    9

fromIntegral
是你的朋友。谢谢你,卢基。这特别有帮助——.
floor。后勤基地2
=>
楼层。后勤基地2。fromIntegral
为什么不看看奇数?日志工作在任何正数上。我没有指定的错误。我从2013年开始做这个家庭作业——为了自学。我应该把这个问题的措词写得更好。请看堆栈溢出上的Haskell标记,详细的解释做得很好。
λ> import Math.NumberTheory.Logarithms

λ> integerLog2 (2^99999)
    99999
it :: Int
λ> :set -XMagicHash

λ> import GHC.Exts

λ> import GHC.Integer.Logarithms

λ> I# (integerLog2# (2^99999))
    99999
it :: Int
λ> integerLog2 1023
    9