由于资源有限,我需要在这里提出一个问题。我一直在为函数式编程而苦苦挣扎,没完没了的 Haskell 教程并没有真正帮助我。所以我想用干净的语言实现将一个字符串拆分为一个字符串" car cow cat "
列表["car","cow","cat"]
。你能给我一个详细的答案(不一定是完整的代码),关于如何迭代这个字符串,尤其是新构造的字符串被添加到列表中的部分吗?
2 回答
我将提供一个简单的解决方案。在 Haskell 中有无数更好的方法可以做到这一点,但对于函数式编程的新手来说,这是我能想到的最简单的方法,无需使用任何特定的 Haskell 函数,如 takeWhile,甚至任何折叠和映射......
您基本上想模拟对列表的迭代,所以这是我的建议:
定义一个函数,该函数将接受一个字符串和一个拆分字符。此函数将返回一个字符串列表 -
spliton :: String -> Char -> [String]
要在列表中移动,我们将要吞噬字符,直到我们击中我们的分裂字符之一。我们还想保存到目前为止我们保存的单词,以及整个单词列表。为此,我们将定义一个子函数来保存状态
spliton' :: String -> Char -> String -> [String] -> [String]
spliton' [] _ sofar res = res ++ [sofar]
我还包括了最简单的子句——一个空字符串。当我们的字符串为空时,我们只想返回到目前为止保存的内容。
现在让我们继续我们实际的递归函数:如果我们点击了分割字符,我们将把到目前为止保存的字符串添加到列表中,并以一个空的当前状态字符串重新开始如果我们没有点击分割字符,我们将字符添加到当前状态字符串
spliton' (currchar:rest) splitby sofar res | currchar==splitby = spliton' rest splitby "" (res++[sofar]) | otherwise = spliton' rest splitby (sofar++[currchar]) res
所以,总结一下我们的代码:
spliton :: String -> Char -> [String]
spliton source splitchar = spliton' source splitchar [] []
spliton' :: String -> Char -> String -> [String] -> [String]
spliton' [] _ sofar res = res ++ [sofar]
spliton' (currchar:rest) splitby sofar res
| currchar==splitby = spliton' rest splitby "" (res++[sofar])
| otherwise = spliton' rest splitby (sofar++[currchar]) res
注意:这不会摆脱空字符串 - 这意味着如果您有许多多余的空格 - 您会将它们添加到列表中。我会让你思考如何处理这种情况 - 希望这可以帮助你开始。
让我们把它分成几个子问题:
- 从字符串中创建一个字符列表,以便我们可以轻松地应用模式匹配。
- 刮掉列表的初始部分(尽可能长的只有空格或只有非空格),并且只在它不是空格时保留它。
- 当列表非空时重复第二步。
第一件事可以使用fromString
. 对于第二步和第三步,我们定义了一个辅助函数:
scrape :: [Char] -> [String]
scrape [] = []
scrape cs=:[c:_]
| isSpace c = scrape (dropWhile isSpace cs)
| otherwise = [toString word:scrape rest]
where
(word,rest) = span (not o isSpace) cs
第一种选择是匹配空列表的基本情况。第二种选择将整个列表cs
与第一个元素匹配c
。如果第一个字符是空格,我们递归地(第 3 步)在同一个列表上调用相同的函数,而没有空格的初始部分。如果第一个字符不是空格,我们使用span :: (a -> Bool) [a] -> ([a], [a])
将列表拆分为单词的初始部分,其余部分。我们将单词 using 存储toString
为字符串,并递归调用scrape
列表的其余部分。
现在,我们只需要一个包装器来使它成为一个具有以下类型的函数String -> [String]
:
split :: String -> [String]
split s = scrape (fromString s)
where
scrape :: [Char] -> [String]
scrape [] = []
scrape cs=:[c:_]
| isSpace c = scrape (dropWhile isSpace cs)
| otherwise = [toString word:scrape rest]
where
(word,rest) = span (not o isSpace) cs
请注意,您可以通过传递一个字符并用 和 替换来轻松地从分隔符中d
抽象出来。或者,您可以选择不传递字符而是传递函数。然后你分别得到和。isSpace c
c == d
(not o isSpace)
((<>) d)
d
isDelim :: Char -> Bool
isDelim c
(not o isDelim)