Haskell - 通过分隔符

时间:2017-10-05 08:17:54

标签: haskell

我正在尝试在Haskell中编写一个程序,用分隔符分割字符串。

我研究过其他用户提供的不同示例。下面发布的代码就是一个例子。

split :: String -> [String]
split [] = [""]
split (c:cs)
   | c == ','  = "" : rest
   | otherwise = (c : head rest) : tail rest
 where
   rest = split cs

示例输入:"1,2,3"。 示例输出:["1","2","3"]

我一直在尝试修改代码,以便输出类似于["1", "," , "2", "," , "3"],其中包括输出中的分隔符,但我不能成功。

例如,我更改了一行:

   | c == ','  = "" : rest

成:

   | c == ','  = "," : rest

但结果变为["1,","2,","3"]

问题是什么?我误解了哪一部分?

3 个答案:

答案 0 :(得分:4)

不是改变代码,希望它与expecations匹配,通常最好先理解代码片段。

split :: String -> [String]
split [] = [""]
split (c:cs) | c == ','  = "" : rest
             | otherwise = (c : head rest) : tail rest
    where rest = split cs

首先,我们更好地分析split的作用。第一个语句简单地说" 空字符串的分割,是一个包含一个元素的列表,空字符串"。这看似合理。现在第二个子句指出:" 如果字符串的头部是逗号,我们会生成一个列表,其中第一个元素是空字符串,然后拆分字符串的剩余部分。&#34 ;.最后一个警卫说" 如果字符串的第一个字符不是逗号,我们会将该字符添加到剩余字符串的第一个分割项,然后是分割的其余元素。剩下的字符串"。请注意split会返回字符串的列表,因此head rest是一个字符串。

因此,如果我们想要将分隔符添加到输出中,那么我们需要在split的输出中将其添加为单独的字符串。哪里?在第一个后卫。我们不应该返回"," : rest,因为头部是 - 通过递归 - 前置,但作为单独的字符串。结果是:

split :: String -> [String]
split [] = [""]
split (c:cs) | c == ','  = "" : "," : rest
             | otherwise = (c : head rest) : tail rest
    where rest = split cs

答案 1 :(得分:3)

那个示例代码风格很差。除非您确切知道自己正在做什么(这些功能不安全,partial functions),否则请勿使用headtail。此外,平等比较通常更好地写为专用模式。

考虑到这一点,该示例变为:

split :: String -> [String]
split "" = [""]
split (',':cs) = "" : split cs
split (c:cs) = (c:cellCompletion) : otherCells
 where cellCompletion : otherCells = split cs

(严格来说,这仍然是不安全的,因为匹配cellCompletion:otherCells并非详尽无遗,但至少它发生在一个明确定义的地方,如果出现任何问题,它会给出明确的错误信息。)

现在IMO,这使得它实际上更加清晰:"" : split cs,意图不是在结果中添加一个空单元格 。相反,它是添加一个单元格,该单元格将被递归堆栈中的进一步调用填充。发生这种情况是因为这些调用再次解析了更深层次的结果,模式匹配cellCompletion : otherCells = split cs,即它们再次弹出第一个单元格并添加实际单元格内容。

因此,如果您将其更改为"," : split,效果就是您构建的所有单元格都已预先终止了,个字符。那不是你想要的。

相反,您想要添加一个不再被触及的附加单元格。那需要更深入的结果:

split (',':cs) = "" : "," : split cs

答案 2 :(得分:1)

如果你正试图写这个功能"对于真实的"我认为一种更清晰的方法是使用break中的Data.List函数,而不是逐字逐句地进行练习。以下表达式:

break (==',') str

将字符串分解为元组(a,b),其中第一部分由最初的"无逗号"组成。 part,第二部分是以逗号开头的更多字符串,如果没有更多的字符串,则为空。

这使得split的定义清晰明了:

split str = case break (==',') str of
                (a, ',':b) -> a : split b
                (a, "")    -> [a]

您可以验证此处理split ""(返回[""]),因此无需将其视为特殊情况。

此版本具有额外的好处,即包含分隔符的修改也很容易理解:

split2 str = case break (==',') str of
                (a, ',':b) -> a : "," : split2 b
                (a, "")    -> [a]

请注意,我已经更详细地编写了这些函数中的模式,而不是为了使其绝对清楚发生了什么,这也意味着Haskell会对每个逗号进行重复检查。出于这个原因,有些人可能更喜欢:

split str = case break (==',') str of
                (a, _:b) -> a : split b
                (a, _)   -> [a]

或者,如果他们仍然希望准确记录他们在每个案例分支中的期望:

split str = case break (==',') str of
                (a, _comma:b) -> a : split b
                (a, _empty)   -> [a]