【问题标题】:Summing the first n odd numbers in Haskell对 Haskell 中的前 n 个奇数求和
【发布时间】:2016-05-17 01:55:09
【问题描述】:

我是 Haskell 和函数式编程的新手,我想知道您将如何在 Haskell 中编写这样的函数。我习惯了命令式语言,但现在在 haskell 中处理递归对我来说是难以捉摸的。请注意,我知道奇数的总和可以用 n^2 完成(即 3 个第一个奇数的总和 1 + 3 + 5 = 9 = 3^2),但想法是通过函数式编程学习递归。

另外,我这样做是作为代数研讨会的一部分,我们还没有看到太多。我需要一种仅通过递归来解决它的方法。

有什么提示吗?谢谢!

【问题讨论】:

标签: haskell recursion


【解决方案1】:
-- the odd numbers (all of them!)
[ 1,3.. ]

-- the first N odd numbers:
take n [1,3..]

-- the sum of the first N odd numbers
sum (take n [1,3..])

把它放在一个函数中:

sumOddNumbers n = sum (take n [1,3..])

【讨论】:

  • 抱歉,我没有提供足够的上下文。这是代数研讨会的一部分,我们还没有看到诸如 take 和 sum 之类的函数。我需要一种仅通过递归来解决它的方法。我将编辑问题。感谢您的回答无论如何是有用的。
【解决方案2】:

首先,您需要描述您的功能。

sumFirstOdds :: Int -> Int

当您考虑递归时,您将需要一个停止点。在这种情况下,一个好的停止点是前零个奇数的总和,也就是零。您可以像这样定义基本情况:

sumFirstOdds 0 = 0

在编写递归函数时,最好养成先声明停止点的习惯。否则你很容易陷入无限循环。

现在我们需要定义当我们想要找到n > 0 时第一个n 奇数之和时会发生什么。

您首先需要找到一种方法来获取第 n 个奇数的值:(n * 2) - 1

现在是递归部分。它可以帮助您通过几个简单的示例来思考问题。如果我们想得到前 3 个奇数的总和,我们会使用这样的公式:

((3 * 2) - 1) + ((2 * 2) - 1) + ((1 * 2) - 1) + 0
  ^               ^               ^             ^
  |               |               |             |
n == 3          n == 2          n == 1        n == 0 (base case)

从请求的数字 3 开始使用相同的算法,并将其添加到对 2 运行相同算法的结果中,然后对 1 执行相同的操作,然后对 0 执行相同的操作,这就是我们达到基本情况的地方然后停下来。

放入 Haskell 代码,你可以这样写:

sumFirstOdds n = ((n * 2) - 1) + (sumFirstOdds (n-1))

针对当前的n 值执行计算,并将该值添加到针对该n-1 值使用的相同函数的结果中。

重申一下最终函数的样子,如下:

sumFirstOdds :: Int -> Int
sumFirstOdds 0 = 0
sumFirstOdds n = ((n * 2) - 1) + (sumFirstOdds (n-1))

注意:这不能很好地处理负输入,但为了简单起见,我把它省略了

【讨论】:

    【解决方案3】:

    由于您应该始终通过平方来对奇数求和,所以我认为通过递归来实现 会很有趣。由于对IntInteger 这样做很愚蠢,因此您应该改为在类型级别工作。这实际上就像用天真的方式对它们求和一样慢,但它更有趣!

    {-# LANGUAGE DataKinds, KindSignatures, TypeFamilies, TypeOperators, GADTs, UndecidableInstances #-}
    
    data Nat = Z | S Nat
    
    infixr 6 :+
    type family (:+) (x :: Nat) (y :: Nat) :: Nat where
      'Z :+ n = n
      'S m :+ n = 'S (m :+ n)
    
    infixr 7 :*
    type family (:*) (x :: Nat) (y :: Nat) :: Nat where
      'Z :* n = 'Z
      'S m :* n = n :+ m :* n
    

    现在要使其可用,您可以使用单例将其降低到术语级别。

    data Natty :: Nat -> * where
      Zy :: Natty 'Z
      Sy :: Natty n -> Natty ('S n)
    
    plus :: Natty m -> Natty n -> Natty (m :+ n)
    plus Zy n = n
    plus (Sy m) n = Sy (plus m n)
    
    times :: Natty m -> Natty n -> Natty (m :* n)
    times Zy _ = Zy
    times (Sy m) n = n `plus` (m `times` n)
    
    square :: Natty n -> Natty (n :* n)
    square n = times n n
    

    【讨论】:

      【解决方案4】:

      这里不需要它,但通常您可以在辅助函数中使用累加器进行递归计算。

      sumodds n = go n 0
         where go 0 a = a
               go k a = go (k-1) (a + 2*k-1)
      
      > map sumodds [0..9]
      [0,1,4,9,16,25,36,49,64,81]
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-03-07
        • 1970-01-01
        • 1970-01-01
        • 2018-09-23
        • 2016-06-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多