【问题标题】:Haskell: Lists and foldlHaskell:列表和折叠
【发布时间】:2012-09-26 17:33:25
【问题描述】:

我有一系列数字:0、1、3、6、10、15、... 基本上,你加 1,然后你加 2,然后加 3,等等。

我必须创建一个函数,将列表中的这一系列数字返回给定数字 n。我想用 foldl。

所以,系列 5 应该返回 [0, 1, 3, 6, 10, 15]

这是我目前所拥有的:

eachElem n = foldl (+) 0 [0..n]

series n = [x | x <- [(eachElem 0), (eachElem 1)..(eachElem n)]]

基本上,我认为列表中的每个元素都是一个 foldl 操作,因此我制作了一个单独的辅助函数 (eachElem) 来完成此操作。

但是,它返回的列表比我想要的要大得多。

例如。系列 3 => [0,1,2,3,4,5,6] 什么时候它应该真正返回 [0,1,3,6]

有什么想法吗?

【问题讨论】:

标签: list haskell fold


【解决方案1】:

scanl 更适合您的工作。

它的类型是scanl :: (a -&gt; b -&gt; a) -&gt; a -&gt; [b] -&gt; [a]——它的类型签名与foldl的相同,但它返回一个增量值列表,而不仅仅是最终结果。

我会把剩下的留给你做练习,因为这看起来像是家庭作业。祝你好运!

【讨论】:

  • 如果我必须使用 foldl,有没有办法可以强制它在创建列表之前评估列表中的“eachElem n”?我认为这就是我的问题所在......我假设在命令式语言中,这种方法会起作用。但我了解到 Haskell 会进行“懒惰评估”
  • 过于懒惰只会影响资源使用,它不会导致与强制不同的结果。强制评估是可能的,但在您的情况下不是必需的。
【解决方案2】:

如果你坚持使用foldl,你可以这样做

series n  = reverse $ foldl f [0] [1..n]
    where f xs@(x:_) y = x+y:xs

在 ghci 中

> series 5
[0,1,3,6,10,15]

但是foldl 的问题是你不能创建无限系列。

你可以有无限的系列像

series = 0:zipWith (+) series [1..]

然后你可以做类似的事情

> take (5+1) series
[0,1,3,6,10,15]

我没有尝试过,但您也可以使用unfoldr 或类似的概念来构建您的列表。

【讨论】:

  • unfoldr (\k -&gt; Just (k*(k+1) `quot` 2, k+1)) 0
【解决方案3】:

scanl 在这里是最好的,但如果你必须使用 fold 试试这个

testso :: Integral a => a -> [a] testso n = reverse $ foldl (\acc x -> head acc + x:acc ) [0] [1,2..n]

输出为 testo 10 [0,1,3,6,10,15,21,28,36,45,55]。

【讨论】:

    【解决方案4】:

    您对series 的定义是错误的。

    [(eachElem 0), (eachElem 1)..(eachElem n)] 变为 [0, 1, eachElem n],这实际上是直到 eachElem n 的每个数字。

    你实际上想要这样做:

    series n = [eachElem x | x <- [0..n]]
    

    【讨论】:

      【解决方案5】:

      定义

      series n = [ x | x <- [(eachElem 0)..(eachElem n)]]
      

      错了!

      例如:

      因为

      eachElem 0 -> 0
      eachElem 3 -> 6
      

      系列 3 评估为

      series 3 -> [(eachElem 0)..(eachElem 3)] -> [0..6] -> [0,1,2,3,4,5,6]
      

      你需要这样的东西

      series' n = [ eachElem x | x <- [0..n]]
      

      测试:

      > let series' n = [ eachElem x | x <- [0..n]]
      > let series n = [ x | x <- [(eachElem 0)..(eachElem n)]]
      
      > series' 3
      > [0,1,3,6]
      
      > series 3
      > [0,1,2,3,4,5,6]
      
      > eachElem 0
      > 0
      
      > eachElem 3
      > 6
      

      【讨论】:

        【解决方案6】:

        当你写[a,b..c]时,a是第一个元素,c是最后一个元素,b是步长,它是列表中每个元素之间的间隔,如果你省略它,它将是默认为 1。

        那么让我们看看你的代码,你可以这样做:

          [x | x <- [(eachElem 0), (eachElem 1)..(eachElem n)]] 
        
        • 在您的列表理解中,x 将首先取值 (eachElem 0) = 0
        • 那么下一个元素将是 (eachElem 0) + (eachElem 1) = 1
        • 那么只要值为 ,那么第 i 个元素将为 (eachElem 0) + i*(eachElem 1 - eachElem 0)

        因此你的结果是:[0,1..(eachElem n)] 产生 [0,1,2,3... 显然不是你所期望的。

        根据 amindfv 的建议,您应该看看 scanl。

        【讨论】:

          【解决方案7】:

          你可以作弊:-)

          series x = foldl (\xs n -> (n*(n+1) `div` 2):xs) [] [x,(x-1)..0]
          

          【讨论】:

            猜你喜欢
            • 2012-02-25
            • 1970-01-01
            • 2020-03-03
            • 2015-08-30
            • 2018-12-24
            • 2018-10-11
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多