我只是一个初级 Haskell 程序员(我学的一点 Haskell 是 5 年前的事),但首先,我会编写函数的自然翻译,并传递累加器(“当前段落”) (为了清楚起见,我添加了类型):
type Line = String
type Para = [Line]
-- Takes a list of lines, and returns a list of paragraphs
paragraphs :: [Line] -> [Para]
paragraphs ls = paragraphs2 ls []
-- Helper function: takes a list of lines, and the "current paragraph"
paragraphs2 :: [Line] -> Para -> [Para]
paragraphs2 [] para = [para]
paragraphs2 ("":ls) para = para : (paragraphs2 ls [])
paragraphs2 (l:ls) para = paragraphs2 ls (para++[l])
这有效:
*Main> paragraphs ["Line 1", "Line 2", "", "Line 3", "Line 4"]
[["Line 1","Line 2"],["Line 3","Line 4"]]
这就是一个解决方案。但是,Haskell 的经验表明几乎总是有库函数可以完成这样的事情:) 一个相关的函数被调用groupBy http://www.zvon.org/other/haskell/Outputlist/groupBy_f.html,它几乎可以工作:
paragraphs3 :: [Line] -> [Para]
paragraphs3 ls = groupBy (\x y -> y /= "") ls
*Main> paragraphs3 ["Line 1", "Line 2", "", "Line 3", "Line 4"]
[["Line 1","Line 2"],["","Line 3","Line 4"]]
哎呀。我们真正需要的是一个“splitBy”,并且它不在图书馆里 http://www.google.com/search?q=haskell%20splitBy,但我们可以自己过滤掉不好的:
paragraphs4 :: [Line] -> [Para]
paragraphs4 ls = map (filter (/= "")) (groupBy (\x y -> y /= "") ls)
或者,如果你想表现得更酷,你可以摆脱争论,用毫无意义的方式来做:
paragraphs5 = map (filter (/= "")) . groupBy (\x y -> y /= "")
我确信还有更短的方法。 :-)
Edit: 短暂的 https://stackoverflow.com/users/20713/ephemient指出(not . null)
比(/= "")
。所以我们可以写
paragraphs = map (filter $ not . null) . groupBy (const $ not . null)
重复的(not . null)
强烈表明我们确实应该将其抽象为一个函数,这就是数据.列表.分割模块 http://byorgey.wordpress.com/2008/12/21/datalistsplit/确实如此,正如下面的答案所指出的。