【问题标题】:Basic summation in HaskellHaskell 中的基本求和
【发布时间】:2011-03-22 03:27:11
【问题描述】:

我正在练习 Haskell,并编写了一个求和函数,它接受两个数字(上限和下限)并进行求和。

即,summation 0 10 将返回 55

我可以让它大部分工作,但很难弄清楚如何只使用两个参数来获得它。

这是我目前所拥有的:

summation :: Integer -> Integer -> Integer -> Integer
summation x y sum =
    if (y<x) then
        sum
    else
        summation x (y-1) (sum+y)

所以这工作正常,但我需要做summation 0 10 0 让它正常工作。我不确定如何在 Haskell 中仅使用两个参数来实现这一点。

【问题讨论】:

    标签: haskell recursion


    【解决方案1】:

    你把它包起来。

    summation :: Integer -> Integer -> Integer
    summation x y = summation' x y 0
    
    summation' :: Integer -> Integer -> Integer -> Integer
    summation' x y sum =
        if (y<x) then
            sum
        else
            summation' x (y-1) (sum+y)
    

    【讨论】:

    • 我不明白。我发誓我尝试了这个确切的解决方案,但它抱怨有No instance for (Show (Integer -&gt; Integer)) --- 不管怎样,谢谢。
    • @dc 你的问题可能是打电话给show (summation 0) 或类似的东西。问题是求和仅适用于 1 个参数,因此它返回一个从 Integer 到 Integer 的函数,没有 Show 的实例。另一方面,show (summation 0 10) 会起作用。您可能有正确的解决方案,但外围问题导致您认为它是错误的。上面的代码从不调用show,所以这不可能是问题。
    • @Joel,感谢您的评论。我不确定那是问题所在。我以前从来没有打电话给show,我也不确定它到底是做什么的,因为我对 Haskell 还是很陌生。我一定是在路上打错了什么。
    • @dc,您可能尝试过这个或类似的方法,并且做了“summation' 0 10”,如果出于技术上不同的原因,这将给出与“summation 0”相同的投诉。 :)
    【解决方案2】:

    快速回答:

    一种简单的方法是使用来自Data.Listsum 函数。

    那么你可以简单地说:

    summation x y = sum [x .. y]
    

    此解决方案假定 x 小于 y,您可以通过以下方式解决此问题:

    summation x y = sum [min x y .. max x y]
    

    定义sum:

    由于您正在学习 Haskell,因此了解 sum 的工作原理可能很重要,而不仅仅是知道它的存在。对我来说,最初要克服的最大障碍是编写太多已经存在的函数。特别是因为我不知道如何有效地编写它们。

    Hoogle 在这方面有很大帮助:它是一个搜索引擎,可让您搜索 Haskell 函数。这对生产力来说是一件好事,因为您将能够花时间解决您的问题,而不是对前奏的一半进行糟糕的重写。它也非常适合学习,因为Hackage上的大部分功能的源代码都有链接。 Prelude 和其他“基本”库(例如 Data.List)的源代码对于初学者来说出乎意料地易于访问,并且可以深入了解“聪明的孩子”如何做事。

    GHCI 中的:browse 命令是我最近发现的,我希望早点发现。

    无论如何,定义sum 的一种方法是使用折叠:

    sum xs y = foldl (+) 0 xs
    

    "pointless" style中的等价物:

    sum = foldl (+) 0
    

    我通常更喜欢第一个公式,但了解第二个公式的工作原理和原因将对您的旅程有很大帮​​助。


    延伸阅读:

    你会注意到我使用了函数foldl。这个函数“折叠”一个输入列表。要“掌握”函数式编程,知道如何fold 既是最基本也是最重要的概念之一。一个很好的参考资源是来自Haskell Wikipage on folds

    【讨论】:

    • 要像 (+) 一样将列表缩减为单个项目,您应该始终使用严格版本 foldl' 因为否则它会构建一个巨大的 ((((1+2)+3)+4) +5)+6 样式表达式在进行任何添加之前。
    • 我实际上只是从Data.List“偷”了这个定义。感谢您的提醒。 haskell.org/haskellwiki/Foldr_Foldl_Foldl' 似乎是讨论您正在谈论的现象的好资源。
    【解决方案3】:

    你可以像高斯那样做。

    summation begin end
        | end < begin = summation end begin
        | otherwise   = n * (2*a + (n-1)*d) `div` 2
      where a = begin
            d = 1
            n = end - begin + 1
    

    代码是来自http://mathcentral.uregina.ca/QQ/database/QQ.02.06/jo1.html 的公然直译(在该页面下方:S = n[2a + (n-1)d]/2

    【讨论】:

    • +1 提醒我们公式和算法一样好。 O(1) 解决方案就在那里。我喜欢它!不过,这可能不是为尝试学习编程的人提供的最佳解决方案。一点点:你可以省略乘以 1。
    • 这个解决方案实际上是 O(n log n log log n) 用于大输入(其中 n 是输入中的位数),进行 3 次乘法,并假设“/2”被实现为二进制移位。重复的加法将是O(n 2^n),因为每次加法都是 O(n),对于具有 n 位的输入,必须执行大约 2^n 次加法。加法/乘法的复杂度是according to Wikipedia
    • 使用 n = 10^7 的公式可以(我认为)343x10^7 操作。在 3 GHz 和每个周期一次操作时,这需要 1.143 秒。由于多种原因,这快得不切实际,但我仍然(不幸的是?)从未做过任何以乘法为瓶颈的工作。谢谢你的链接,不过……现在我要花一整天时间在维基百科上;)
    猜你喜欢
    • 2015-05-25
    • 1970-01-01
    • 2010-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-26
    相关资源
    最近更新 更多