2012-03-19 70 views
5

即时搜索我的Haskell类的解决方案。拆分列表并从子列表中获取总和?

我有一个数字列表,我需要返回SUM列表的每一部分。零件除以0.我需要使用FOLDL功能。

实施例:
初始列表:[1,2,3,0,3,4,0,5,2,1]
子列表[[1,2,3],[3,4], [5,2,1]
结果[6,7,7]

我有在初始列表中找到0的函数:

findPos list = [index+1 | (index, e) <- zip [0..] list, e == 0] 

(返回[4,6]为初始列表from example)

以及用FOLDL制作SUM的功能:

sumList list = foldl (+) 0 list 

但我完全没有把它一起:/

----我的解决方案
最后,我发现完全不同的东西,你们建议。
我花了整整一天,使其:/

groups :: [Int] -> [Int] 
groups list = [sum x | x <- makelist list] 

makelist :: [Int] -> [[Int]] 
makelist xs = reverse (foldl (\acc x -> zero x acc) [[]] xs) 

zero :: Int -> [[Int]] -> [[Int]] 
zero x acc | x == 0 = addnewtolist acc 
      | otherwise = addtolist x acc 

addtolist :: Int -> [[Int]] -> [[Int]] 
addtolist i listlist = (i : (head listlist)) : (drop 1 listlist) 

addnewtolist :: [[Int]] -> [[Int]] 
addnewtolist listlist = [] : listlist 
+0

我认为'结果'应该是'[6,7,8]'而不是'[6,7,7]'。 – Landei 2012-03-20 07:50:19

回答

2

既然您已经完成了自己的问题,我将向您显示一个稍微不太详细的版本。 Foldr在我看来这个问题看起来好一点*,但是因为你问了foldl我会用两种功能向你展示我的解决方案。

此外,您的实例似乎是不正确,[5,2,1]的总和为8,而不是7.

foldr版本。

makelist' l = foldr (\x (n:ns) -> if x == 0 then 0:(n:ns) else (x + n):ns) [0] l 

在这个版本中,我们遍历列表中,如果当前元素(x)是一个0,我们添加新元素到累加器列表(N:NS)。否则,我们将当前元素的值添加到累加器的前面元素的值中,并用此值替换累加器的前面值。

一步一步:

  1. acc = [0], x = 1. Result is [0+1]
  2. acc = [1], x = 2. Result is [1+2]
  3. acc = [3], x = 5. Result is [3+5]
  4. acc = [8], x = 0. Result is 0:[8]
  5. acc = [0,8], x = 4. Result is [0+4,8]
  6. acc = [4,8], x = 3. Result is [4+3,8]
  7. acc = [7,8], x = 0. Result is 0:[7,8]
  8. acc = [0,7,8], x = 3. Result is [0+3,7,8]
  9. acc = [3,7,8], x = 2. Result is [3+2,7,8]
  10. acc = [5,7,8], x = 1. Result is [5+1,7,8] = [6,7,8]

有你有它!

foldl版本。与上面类似,但产生一个反向列表,因此在该函数的开始处使用反向来反向列表。

makelist l = reverse $ foldl (\(n:ns) x -> if x == 0 then 0:(n:ns) else (x + n):ns) [0] l 

*折叠从右边的列表允许利弊(:)功能被自然使用的,使用我的方法与左折叠产生一个反向的列表。 (有可能做的左折叠版本,我没有想到的,消除这个平凡简单的方法。)

+0

真棒,谢谢:) – m4recek 2012-03-20 00:50:51

6

我要给你一些提示,而不是一个完整的解决方案,因为这听起来像它可能是一个家庭作业。

我喜欢你建议的步骤细目。对于第一步(从具有零标记的数字列表转到列表列表),我建议做一个明确的递归;试试这个为模板:

splits []  = {- ... -} 
splits (0:xs) = {- ... -} 
splits (x:xs) = {- ... -} 

您也可以滥用groupBy如果你细心的话。

对于第二步,它看起来像你几乎在那里;您需要的最后一步是查看map :: (a -> b) -> ([a] -> [b])函数,该函数采用普通函数并在列表中的每个元素上运行该函数。

作为一项奖励练习,您可能想要考虑如何以一次完成一次完成所有事情。如果你追踪foldr/foldl所需的各种参数的类型,这是可能的 - 甚至不会太困难!

添置因为这个问题变了:

因为它看起来像你已经研究出了解决办法,我现在感觉很舒服给予一定的破坏者。 =)

我提出了两种可能的实现方式;正如你所建议的那样,一步步一步一步地完成。步骤一步一个看起来是这样的:

splits []  = [] 
splits (0:xs) = [] : splits xs 
splits (x:xs) = case splits xs of 
    []  -> [[x]] 
    (ys:yss) -> ((x:ys):yss) 

groups' = map sum . splits 

或者这样:

splits' = groupBy (\x y -> y /= 0) 
groups'' = map sum . splits' 

全在一次的版本可能是这样的:

accumulate 0 xs  = 0:xs 
accumulate n (x:xs) = (n+x):xs 

groups''' = foldr accumulate [0] 

要检查你是否了解这些,下面是一些你可能会喜欢尝试的练习:

  • splitssplits'做什么[1,2,3,0,4,5][1,2,0,3,4,0][0][]?用ghci检查你的预测。
  • 预测groups(包括你的)四个版本中的每一个输出的输入类似[][1,2,0,3,4,0],然后用ghci测试你的预测。
  • 修改groups'''以展现其他实现之一的行为。
  • 修改groups'''使用foldl而不是foldr
+0

如果这不是作业,你可以使用Hackage和splitOn [0]分割包。 – Jedai 2012-03-19 18:18:40

+0

嗨,谢谢你的帮助:)可悲的是,它对我来说并没有什么帮助。我是客人,我需要“假人的例子”。我真的对haskell很陌生,所以我遇到了这样的问题:“如何添加一些东西到列表中的第一个列表”和“如何写类型”等等。无论如何,你能写出你建议的解决方案吗?我感兴趣,它应该如何:) – m4recek 2012-03-19 23:06:05

+0

@ user1279158完成。 – 2012-03-20 01:07:34

1

正如你已经解决了这个问题,另一个版本:

subListSums list = reverse $ foldl subSum [0] list where 
    subSum xs 0 = 0 : xs 
    subSum (x:xs) n = (x+n) : xs 

(假设你在列表中只有非负数)

+0

非负面假设在代码中出现在哪里? – 2012-03-20 20:51:09

+0

如果一个组的数量总和为零,则这个零将被下一个组“覆盖”,所以这个总和不会出现在最终列表中。 – Landei 2012-03-20 23:14:58

+0

我想你可能误解了你自己的代码!没有你的代码的一部分检查当前运行总和是否等于'0';此外,在更经验的层面上,'subListSums [2,-2,0,2,-2]'似乎在ghci中是正确的。 – 2012-03-21 00:20:53