2

由于资源有限,我需要在这里提出一个问题。我一直在为函数式编程而苦苦挣扎,没完没了的 Haskell 教程并没有真正帮助我。所以我想用干净的语言实现将一个字符串拆分为一个字符串" car cow cat "列表["car","cow","cat"]。你能给我一个详细的答案(不一定是完整的代码),关于如何迭代这个字符串,尤其是新构造的字符串被添加到列表中的部分吗?

4

2 回答 2

3

我将提供一个简单的解决方案。在 Haskell 中有无数更好的方法可以做到这一点,但对于函数式编程的新手来说,这是我能想到的最简单的方法,无需使用任何特定的 Haskell 函数,如 takeWhile,甚至任何折叠和映射......

您基本上想模拟对列表的迭代,所以这是我的建议:

  1. 定义一个函数,该函数将接受一个字符串和一个拆分字符。此函数将返回一个字符串列表 -spliton :: String -> Char -> [String]

  2. 要在列表中移动,我们将要吞噬字符,直到我们击中我们的分裂字符之一。我们还想保存到目前为止我们保存的单词,以及整个单词列表。为此,我们将定义一个子函数来保存状态

    spliton' :: String -> Char -> String -> [String] -> [String]

    spliton' [] _ sofar res = res ++ [sofar]

    我还包括了最简单的子句——一个空字符串。当我们的字符串为空时,我们只想返回到目前为止保存的内容。

  3. 现在让我们继续我们实际的递归函数:如果我们点击了分割字符,我们将把到目前为止保存的字符串添加到列表中,并以一个空的当前状态字符串重新开始如果我们没有点击分割字符,我们将字符添加到当前状态字符串

    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

注意:这不会摆脱空字符串 - 这意味着如果您有许多多余的空格 - 您会将它们添加到列表中。我会让你思考如何处理这种情况 - 希望这可以帮助你开始。

于 2014-10-14T10:54:18.980 回答
2

让我们把它分成几个子问题:

  1. 从字符串中创建一个字符列表,以便我们可以轻松地应用模式匹配。
  2. 刮掉列表的初始部分(尽可能长的只有空格或只有非空格),并且只在它不是空格时保留它。
  3. 当列表非空时重复第二步。

第一件事可以使用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 cc == d(not o isSpace)((<>) d)disDelim :: Char -> BoolisDelim c(not o isDelim)

于 2016-09-23T18:17:10.503 回答