是否有可能以不同的方式编写与“教科书”一样快的阶乘函数?

时间:2016-11-08 04:59:46

标签: haskell recursion optimization

我认为这是Haskell中阶乘函数的一般形式:

factorial :: (Integral a) => a -> a
factorial n = product [1..n]

我知道这是最优雅的方式,但是当我编写自己的递归函数时,它会慢得多:

factorial :: (Integral a) => a -> a
factorial 1 = 1
factorial n = n * factorial (n - 1)

第一个解决方案是否必须在内部完成第一个解决方案所做的所有事情?怎么这么快?是否有可能像第一个解决方案一样快速地编写内容而不使用花式列表表示法或产品函数?

2 个答案:

答案 0 :(得分:10)

第一个版本比GHC更容易优化。特别是product uses foldl

def sign_up(request):
    if request.method == "POST":
        form = IdForm(request.POST)
        if form.is_valid():
            post = form.save()
            post.save()
            id = post.id_text
            return HttpResponse('thank you, your ID number is: '+id)
        else:
            return HttpResponse('Form is invalid')
    else:
        form = IdForm()
    return render(request, 'checkin/base.html', {'form': form})

当应用于product = foldl (*) 1 (仅[1..n])时,它受fusion的约束。简而言之,GHC精心设计了重写规则,旨在优化中间数据结构,从而立即消耗创建列表的代码段(在1 `enumFromTo` n的情况下,factorial是消费者和foldl (*) 1制片人。)

请注意,您可以执行GHC所做的事情(1 `enumFromTo` n)并获得相同的效果。

另外,你的第二个函数甚至不是尾递归的。通过传递累加器,你可以很容易地解决这个问题:

factorial = foldl (*) 1 . enumFromTo 1

与此同时,对于大多数数字类型,您都希望算术是严格的。归结为将factorial :: (Integral a) => a -> a factorial n = go n 1 where go 0 m = m go n m = go (n-1) (n*m) 添加到BangPatternsn

答案 1 :(得分:2)

也许是这样的:

f n = foldl (*) 1 [1..n]

您可以在foldr或foldl'上更改foldl。它会改变速度