对不起,我不完全得到FP然而,我想线的序列分割成线的序列的序列,假设一个空行作为段落划分,我可以在这样蟒做到这一点:

def get_paraghraps(lines):
    paragraphs = []
    paragraph = []
    for line in lines:
        if line == "": # I know it could also be  "if line:"
            paragraphs.append(paragraph)
            paragraph = []
        else:
            paragraph.append(line)
    return paragraphs

您会如何去在二郎山或哈斯克尔做什么呢?

有帮助吗?

解决方案

我只是一个开始哈斯克尔程序员(和小哈斯克尔我的教训是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"]]

所以这是一个解决方案。但随后,哈斯克尔的经验表明,有几乎总是做这样的事情:)一个相关函数库函数被调用的 GROUPBY 和它几乎工作的:

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”和它不是在图书馆 ,但我们可以过滤掉不好的自己:

paragraphs4 :: [Line] -> [Para]
paragraphs4 ls = map (filter (/= "")) (groupBy (\x y -> y /= "") ls)

或者,如果你想成为很酷,你可以摆脱论证,并做无谓的方式:

paragraphs5 = map (filter (/= "")) . groupBy (\x y -> y /= "")

我敢肯定有一个更短的方式。 :-)

修改 ephemient 指出(not . null)(/= "")吸尘器。所以,我们可以这样写

paragraphs = map (filter $ not . null) . groupBy (const $ not . null)

在重复(not . null)是一个强烈的信号,我们真的应该抽象这一点到一个函数,这是什么的 Data.List.Split模块确实,如在下面的答案指出。

其他提示

我也想学习哈斯克尔。用于此问题的解决方案可以是:

paragraphs :: [String] -> [[String]]
paragraphs [] = []
paragraphs lines = p : (paragraphs rest)
    where (p, rest) = span (/= "") (dropWhile (== "") lines)

,其中我使用的功能从数据.LIST 。我使用的手机已经可以从前奏,但你可以找到他们的链接文档。

我们的想法是使用找到的span (/= "")第一段。这将返回段落,并按照行。然后,我们递归我称之为rest线的较小列表上。

分裂出来的第一段之前,我们滴使用dropWhile (== "")任何空行。这是重要的食用分离段的空行(一个或多个)。我的第一次尝试是这样的:

paragraphs :: [String] -> [[String]]
paragraphs [] = []
paragraphs lines = p : (paragraphs $ tail rest)
    where (p, rest) = span (/= "") lines

,但是当我们到达最终段自rest然后空字符串这个失败:

*Main> paragraphs ["foo", "bar", "", "hehe", "", "bla", "bla"]
[["foo","bar"],["hehe"],["bla","bla"]*** Exception: Prelude.tail: empty list

删除空行解决了这个,它也使代码治疗任何数量的空行作为一个段落分隔,这是我希望为用户。

在干净的解决方案将是使用从分裂适当的东西包。

您需要先安装,但随后Data.List.Split.splitWhen null应该很好做的工作。

想递归。

get_paragraphs []      paras para = paras ++ [para]
get_paragraphs ("":ls) paras para = get_paragraphs ls (paras ++ [para]) []
get_paragraphs (l:ls)  paras para = get_paragraphs ls paras (para ++ [l])

您想组线,所以从groupBy Data.List似乎是个不错的人选。它使用自定义功能来确定哪些行是“平等”这样一个可以提供的东西,使得在同一段线“平等”。例如:

import Data.List( groupBy )

inpara :: String -> String -> Bool
inpara _ "" = False
inpara _ _  = True

paragraphs :: [String] -> [[String]]
paragraphs = groupBy inpara

这有一定的局限性,因为inpara只能比较两个相邻行和更复杂的逻辑不适合由groupBy给出的框架。更元素的解决方案,如果是更灵活。使用基本递归一个可以写成:

paragraphs [] = []
paragraphs as = para : paragraphs (dropWhile null reminder)
  where (para, reminder) = span (not . null) as
                           -- splits list at the first empty line

span拆分在该点的提供的函数变为假(第一空行)的列表,dropWhile移除前导元素的量,提供的函数是真实的(任何前导空线)。

总比不晚。

import Data.List.Split (splitOn)

paragraphs :: String -> [[String]]
paragraphs s = filter (not . null) $ map words $ splitOn "\n\n" s

paragraphs "a\nb\n\nc\nd"                == [["a", "b"], ["c", "d"]]
paragraphs "\n\na\nb\n\n\nc\nd\n\n\n"    == [["a", "b"], ["c", "d"]]
paragraphs "\n\na\nb\n\n \n  c\nd\n\n\n" == [["a", "b"], ["c", "d"]]
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top