【问题标题】:Split string to a list of strings in Clean将字符串拆分为 Clean 中的字符串列表
【发布时间】:2014-10-14 09:24:53
【问题描述】:

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

【问题讨论】:

  • 没有具体回答您的问题,但您可以使用来自Data.List.Split 的内置函数:filter (not .null) $ splitOn " " " car cat dog cow moop" 返回["car","cat","dog","cow","moop"]
  • Clean 中可能有一个库函数,类似于 Haskell 的 words 函数。
  • @Arnon 谢谢你的回答,我对使用什么有想法,但由于这是我第一次使用函数式编程,如果可能的话,我可能需要更多帮助。

标签: string split functional-programming clean-language


【解决方案1】:

我将提供一个简单的解决方案。在 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

注意:这不会消除空字符串 - 这意味着如果您有许多多余的空格 - 您会将它们添加到列表中。我会让你考虑如何处理这个案例 - 希望这可以帮助你开始。

【讨论】:

  • 问题的格式有问题。检查一下,我认为您的代码需要更新。
  • 谢谢,虽然我必须在 Clean 中编程,但我现在应该可以了。
【解决方案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]) 将列表拆分为单词的初始部分,其余部分。我们使用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)

【讨论】:

    猜你喜欢
    • 2019-04-16
    • 2015-09-01
    • 2011-11-13
    • 1970-01-01
    • 1970-01-01
    • 2021-07-19
    • 1970-01-01
    • 1970-01-01
    • 2020-02-12
    相关资源
    最近更新 更多