为什么在haskell?;

为什么在haskell?;,haskell,recursion,Haskell,Recursion,我是haskell的新手,我在haskell中定义了一个func: febs :: (Integral a)=> a -> a febs n | n<=0 =0 | n==1 =1 | n==2 =1 | otherwise =febs(n-1)+febs(n-2) 有没有办法提高我的haskell func速度?尝试使用优化编译。使用带-O2的GHC 7.4.1,您的程序运行速度非常快: $ time ./test 832040 rea

我是haskell的新手,我在haskell中定义了一个func:

febs :: (Integral a)=> a -> a
febs n 
    | n<=0 =0
    | n==1 =1
    | n==2 =1
    | otherwise =febs(n-1)+febs(n-2)

有没有办法提高我的haskell func速度?

尝试使用优化编译。使用带-O2的GHC 7.4.1,您的程序运行速度非常快:

$ time ./test 
832040

real    0m0.057s
user    0m0.056s
sys     0m0.000s
这是通过
main=print(febs 30)
实现的


关于Chris Taylor回答中的多态性考虑,这里是带有OP多态性斐波那契函数的febs 40

$ time ./test 
102334155

real    0m5.670s
user    0m5.652s
sys     0m0.004s
这里是一个非多态的,即OP的签名替换为
Int->Int

$ time ./test 
102334155

real    0m0.820s
user    0m0.816s
sys     0m0.000s
根据Tikhon Jelvis的评论,很有意思的是,看看加速是因为用
Int
替换
Integer
,还是因为去除了多态性。下面是同样的程序,除了根据Daniel Fischer的评论将
febs
移动到新文件,以及使用
febs::Integer->Integer

$ time ./test 
102334155

real    0m5.648s
user    0m5.624s
sys     0m0.008s
同样,使用不同文件中的
febs
,并使用与最初相同的多态签名:

$ time ./test 
102334155

real    0m16.610s
user    0m16.469s
sys     0m0.104s

这是一个奇怪的比较,原因如下:

  • 您不会说您是否正在编译Haskell代码,或者使用什么选项。如果您只是在ghci中运行它,那么它当然会很慢—您正在比较解释代码和编译代码

  • <> L> >p>您的Haskell代码是多态的,而C++代码是单形的(即,您使用了类型类<代码>积分a= > a> a < /COD>,而不是具体类型<代码> int > int <代码>。因此,Haskell代码比C++代码更通用,因为它可以处理任意大整数,而不是限制在<代码> INT/COD>范围内。编译器可能会对此进行优化,但我不确定

    如果我把下面的代码放在fib.hs文件中

    fibs :: Int -> Int
    fibs n = if n < 3 then 1 else fibs (n-1) + fibs (n-2)
    
    main = print (fibs 30)
    
    fibs::Int->Int
    fibs n=如果n<3,则1个其他fibs(n-1)+fibs(n-2)
    主=打印(fibs 30)
    

    然后用ghc-O2fib.hs编译它,然后它运行得足够快,对我来说是即时的。你应该尝试一下,看看它与C++代码的比较。

    你也可以这样写函数:

    fibs = 0:1:zipWith (+) fibs (tail fibs)
    
    它非常快,即使是大n也会立即执行:

    Prelude> take 1000 fibs 
    

    该函数传统上称为“fib”或“fibs”,因为它给出斐波那契数。只是让我的学究作风消失:关于多态性:在这种情况下,这真的有影响吗?我猜当你使用它的时候,这个函数会被内联或者其他什么,但我不是专家。(假设您在上进行了优化。)哦,假设它在调用站点上具有
    Int
    类型。仔细想想,这不是一个公平的假设。事实上,我不确定。我想你必须看看核心,但我没有足够的经验去做这件事!实际上,我可以运行两个版本(使用两个签名)并比较运行时间。我所说的“我”,实际上是指“你”,因为我必须引导到Linux:P.@TikhonJelvis,它扮演着一个角色。因为它是递归的,所以不能内联。在使用优化进行编译时(始终假定为GHC),如果它在定义的同一源文件中以单形式使用,您将获得专业化。如果没有类型签名,则在
    main
    中,它将位于
    Integer
    ,因此比使用
    Int
    时稍微慢一些。如果您在不同的文件中定义它,而不是在中使用它,除非您将其设置为
    {-#inlineable#-}
    ,否则您会得到非常慢的多态代码。@DanielFischer:这正是我想知道的。谢谢是的,但这是渐进的不同。我认为真正的问题是把C++和Haskell进行比较,所以使用不同的算法就会过时。一般来说,这是一个很好的建议。或者更酷一点,像这样:
    fibs=0:scanl(+)1 fibs
    非常感谢,这是一个很好的解决方案,我可以从这些代码中感受到haskell的力量。@Ed'ka为什么中途停下来
    fix((0:).scanl(+)1)
    。前几天我看到了一个非常好的例子:`let fibs@(t:fibs')=1:zipWith(+)fibs-fibs',在fibsThanks中测试多态与单态版本(我正在运行Windows,所以获得运行时很痛苦!)一个
    Integral
    类型默认为
    Integer
    。我很好奇性能的变化是否仅仅是因为
    Integer
    vs
    Int
    或者多态性也很重要。您在与
    main
    相同的文件中定义了函数,不是吗?在单独的文件中,使用多态代码,
    febs40
    的运行时间为15.5s,
    Integer->Integer
    的运行时间为5.4s,
    Int->Int
    的运行时间为0.86s@TikhonJelvis这会回答你的问题。对于gcc-O3,0.3s。您必须在定义站点告诉GHC,该函数或者应该专门用于某些类型(
    {-#specialize foo::Int->Int,Word->Integer#-}
    [也接受美国拼写]),或者-需要GHC>=7[可能是7.2,不确定]-带有
    {-#inlineable foo}
    pragma,它应该公开接口文件的内联/优化/专业化功能。然后(总是使用-O2)它将自动完成,至少如果嵌套不是太深的话(我可以想象,如果调用树是1000个多态函数深的话,优化者会放弃)。一般来说,是的。使用
    Inlineable
    ,将在使用站点生成专用版本。如果在许多模块中的一个特定类型上使用多态函数,每个模块都有自己的专用版本,那么最好使用
    specialize
    pragma来减少(编译)代码大小。
    Prelude> take 1000 fibs