m4r*_*cek 5 int haskell numbers
我正在寻找我的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]
Run Code Online (Sandbox Code Playgroud)
(从示例中返回[4,6]作为初始列表)
和用FOLDL制作SUM的功能:
sumList list = foldl (+) 0 list
Run Code Online (Sandbox Code Playgroud)
但我完全没能把它放在一起:/
----我的解决方案
最后,我发现了一些与你们建议完全不同的东西.
花了我一整天才成功:/
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
Run Code Online (Sandbox Code Playgroud)
我将给你一些提示,而不是一个完整的解决方案,因为这听起来像是一个家庭作业.
我喜欢你建议的步骤细分.对于第一步(从具有零标记的数字列表到列表列表),我建议进行显式递归; 试试这个模板:
splits [] = {- ... -}
splits (0:xs) = {- ... -}
splits (x:xs) = {- ... -}
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
或者像这样:
splits' = groupBy (\x y -> y /= 0)
groups'' = map sum . splits'
Run Code Online (Sandbox Code Playgroud)
一次性版本可能如下所示:
accumulate 0 xs = 0:xs
accumulate n (x:xs) = (n+x):xs
groups''' = foldr accumulate [0]
Run Code Online (Sandbox Code Playgroud)
要检查您是否理解这些,您可以尝试以下几种练习:
splits和splits'做[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.
现在您已经自己完成了该问题,我将向您展示一个稍微不那么冗长的版本。 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
Run Code Online (Sandbox Code Playgroud)
在这个版本中,我们遍历列表,如果当前元素(x)是0,我们将一个新元素添加到累加器列表(n:ns)。否则,我们将当前元素的值添加到累加器前面元素的值中,并用该值替换累加器前面的值。
一步步:
acc = [0], x = 1. Result is [0+1]acc = [1], x = 2. Result is [1+2]acc = [3], x = 5. Result is [3+5]acc = [8], x = 0. Result is 0:[8]acc = [0,8], x = 4. Result is [0+4,8]acc = [4,8], x = 3. Result is [4+3,8]acc = [7,8], x = 0. Result is 0:[7,8]acc = [0,7,8], x = 3. Result is [0+3,7,8]acc = [3,7,8], x = 2. Result is [3+2,7,8]acc = [5,7,8], x = 1. Result is [5+1,7,8] = [6,7,8]你有它!
还有foldl版本。工作原理与上面类似,但会产生一个反转列表,因此在此函数的开头使用reverse来取消反转列表。
makelist l = reverse $ foldl (\(n:ns) x -> if x == 0 then 0:(n:ns) else (x + n):ns) [0] l
Run Code Online (Sandbox Code Playgroud)
*从右侧折叠列表可以自然地使用 cons (:) 函数,使用我的方法与左侧折叠会产生一个反向列表。(可能有一种更简单的方法来完成我没有想到的左折叠版本,从而消除了这种琐碎的事情。)