【问题标题】:Sum of separated elements in a list [duplicate]列表中分离元素的总和[重复]
【发布时间】:2012-04-07 17:38:29
【问题描述】:

可能重复:
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)

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

我试图通过编辑我的剪切函数来解决这个问题:

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

但我不知道如何调整它,所以它会将总和分开。现在它只是将所有元素的总和作为输出。

【问题讨论】:

    标签: haskell


    【解决方案1】:

    你可以把你的问题分成两个任务

    1. 将列表拆分为零部分。
    2. 总和。

    对于第一个任务,我们有 Data.List.Split 模块,它导出 splitOn 函数。 它正是我们需要的:

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

    对于第二个任务,众所周知的map-function 将函数应用于列表的每个元素。 在我们的例子中,这个函数是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]
    

    【讨论】:

    • @mtzero 如果这不是真正的问题,您应该将您的问题标记为作业。
    【解决方案2】:

    对于家庭作业,您绝对应该遵循戴夫的回答。但是,这里有一个更高级的解决方案,使用groupBy 作为穷人的split

    import Data.List (groupBy)
    
    map sum $ groupBy (const (/=0)) list
    

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

    [解释]

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

    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
    

    【讨论】:

    • 谢谢,这对我来说是完美的解决方案,因为我需要使用 foldl (+) 0。所以我有这样的:map (foldl (+) 0) (groupBy ... [] )
    【解决方案3】:

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

    1. 将带有0 分隔符的奇怪列表拆分为更常规的列表列表。
    2. 对每个列表求和。

    所以

    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?
    

    【讨论】:

      猜你喜欢
      • 2021-11-16
      • 1970-01-01
      • 1970-01-01
      • 2013-12-28
      • 1970-01-01
      • 2021-08-25
      • 2014-12-31
      • 1970-01-01
      • 2022-01-11
      相关资源
      最近更新 更多