列表中分隔元素的总和

时间:2012-04-07 17:38:30

标签: haskell

  

可能重复:
  Split list and make sum from sublist?

我试图解决这个问题。 我需要从列表中做一个元素的总和,它们只用“0”相互分开。 所以例如我可以输入这样的东西:[1,2,3,0,3,4,0,2,1] 输出应为[6,7,3]。

到目前为止,我设法做了类似的事情:

cut (x:xs) | x > 0 = x : (cut xs)
       | otherwise = []

first  (xs) = ( (foldl (+) 0 (cut          (xs)))   ) : []
second (xs) = ( (foldl (+) 0 (cut (reverse (xs))))  ) : []

test (xs) = first(xs) ++ second(xs)

问题是这只适用于我的列表中只有一个“0”的实例。

我试图通过编辑剪切功能来解决这个问题:

cut [] = []
cut (x:xs) | x > 0 = foldl (+) 0 ( x : cut xs) : []
       | x == 0 = (cut xs)

但我无法弄清楚如何调整它,所以它将分开总和。现在它只是抛出所有元素的总和作为输出。

3 个答案:

答案 0 :(得分:5)

您可以将问题分成两个任务

  1. 将列表拆分为零部分。
  2. Sum parts。
  3. 对于第一个任务,我们有Data.List.Split模块导出splitOn函数。 它正是我们所需要的:

    > splitOn [1] [0,0,0,1,0,0,0,1,0]
    [[0,0,0],[0,0,0],[0]]
    

    对于第二个任务,有一个众所周知的map - 函数,它将一个函数应用于列表的每个元素。 在我们的例子中,这个函数是sum

    > map sum [[1,2,3],[4,5,6],[7,8,9]]
    [6,15,24]
    

    所以:

    > :m +Data.List.Split
    > map sum . splitOn [0] $ [1,2,3,0,3,4,0,2,1] 
    [6,7,3]
    

答案 1 :(得分:1)

即使您无法安装以安装split软件包并使用Matvey建议的Data.List.Split,您仍然可以使用一般方法:

  1. 将带有0分隔符的奇怪列表拆分为更常规的列表列表。
  2. 总结每个清单。
  3. 所以

    yourFunction = map sum . split
    

    现在我们必须写split

    split :: [Int] -> [[Int]]
    

    一般来说,当我们想要将列表分开来合成新内容时,我们需要使用折叠。

    split = foldr cons nil where
    

    nil这里应该是您想要的split []

        nil = --TODO: exercise for you; clue: NOT []
    

    cons同时结合你的一个数字,以及前一步的回答。显然,根据数字是否为0,您需要做不同的事情。

        cons 0 xss        = --TODO
        cons x (xs : xss) = --TODO; why will this pattern match never fail?
    

答案 2 :(得分:1)

对于家庭作业,你一定要遵循戴夫的回答。但是,这是一个更高级的解决方案,使用groupBy作为穷人split

import Data.List (groupBy)

map sum $ groupBy (const (/=0)) list

这可能看起来很可爱,但请注意,在子列表的开头仍然存在零,因此如果重要,您无法使用此解决方案(例如,如果您需要产品而不是总和)< / p>

<强> [说明]

groupBy查看当前组的第一个元素是否与列表的当前元素“合在一起”。在这种情况下,当前元素将添加到组中,否则将启动新组。 E.g。

groupBy (\x y -> x `mod` y == 0) [81,3,9,25,5]
--[[81,3,9],[25,5]]

此处81 'mod' 381 'mod' 9的测试成功,但81 'mod' 25的测试未成功,后者启动了一个新组。同样,25 'mod' 5成功。

但是在我们的例子中,只要它们不是0,所有元素都“适合”当前组,所以我们甚至不必查看第一个元素。如果找到0,则启动新组。

const (/=0)仅表示\_ y -> y /= 0,所以无论第一个参数是什么,它只测试第二个元素不是0.要了解原因,请查看定义:

const :: a -> b -> a
const a _ =  a

现在我们的lambda可以写成

\x y -> const (/= 0) x y

const来看,只有两个论点中的第一个“幸存”,我们有

\x y -> (/= 0) y

......或......

\_ y -> y /= 0