我试图解决这个问题。 我需要从列表中做一个元素的总和,它们只用“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)
但我无法弄清楚如何调整它,所以它将分开总和。现在它只是抛出所有元素的总和作为输出。
答案 0 :(得分:5)
您可以将问题分成两个任务
对于第一个任务,我们有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,您仍然可以使用一般方法:
0
分隔符的奇怪列表拆分为更常规的列表列表。所以
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' 3
和81 '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