我正在尝试在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"]
。
问题是什么?我误解了哪一部分?
答案 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),否则请勿使用head
和tail
。此外,平等比较通常更好地写为专用模式。
考虑到这一点,该示例变为:
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]