简体   繁体   English

在Clean中将字符串拆分为字符串列表

[英]Split string to a list of strings in Clean

Because of the limited amount of resources, I need to propose a question here . 由于资源有限,我需要在这里提出一个问题。 I have been struggling with functional programming, the endless Haskell tutorials don't really help me. 我一直在努力进行函数式编程,无休止的Haskell教程并没有真正帮助我。 So what I want to achieve, in Clean language, is to split a string like " car cow cat " to a list of strings ["car","cow","cat"] . 因此,我要用Clean语言实现的目标是将" car cow cat "类的字符串拆分为字符串列表["car","cow","cat"] Can you provide me a detailed answer (does not have to be complete code), on how to iterate through this string, and especially the part when the newly constructed strings are added to the list? 您能为我提供一个详细的答案(不一定是完整的代码),有关如何遍历此字符串,尤其是将新构造的字符串添加到列表中的部分吗?

I'm going to offer a simple solution. 我将提供一个简单的解决方案。 There are infinitely better ways of doing this in Haskell, but it's the simplest I can think for someone new in functional programming, without using any specifically Haskell function like takeWhile, or even any folds and maps... 在Haskell中,有无限多种更好的方法可以做到这一点,但这是我想到的最简单的函数式编程方法,而无需使用任何特定的Haskell函数(例如takeWhile或什至没有折叠和贴图)。

You basically want to simulate iterating over a list, so here is what I suggest: 您基本上想模拟对列表的迭代,所以这是我的建议:

  1. Define a function that will take a string and a split-by character. 定义一个将使用字符串和分隔符的函数。 This function will return a list of strings - spliton :: String -> Char -> [String] 此函数将返回字符串列表spliton :: String -> Char -> [String]

  2. To move over the list, we'll want to gobble up characters until we hit one of our splitting characters. 要移至列表上方,我们将要吞噬字符,直到我们击中分割字符之一。 We'll also want to save the word we've saved up until now, and the entire list of words. 我们还将要保存到目前为止保存的单词以及整个单词列表。 For that, we'll define a subfunction that will save the states 为此,我们将定义一个子函数来保存状态

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

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

    I've also included the simplest clause - an empty string. 我还包括了最简单的子句-空字符串。 When our string is empty, we'll just want to return what we have saved so far. 当我们的字符串为空时,我们只想返回到目前为止保存的内容。

  3. Now lets move on to our actual recursive function: If we hit our split character, we'll add the string we have saved so far to the list and restart with an empty current-state string If we don't hit the split character, we'll add the character to the current-state string 现在,让我们继续进行实际的递归函数:如果我们击中分隔符,我们将到目前为止保存的字符串添加到列表中,并以空的当前状态字符串重新开始。如果我们未击中分隔符,我们将字符添加到当前状态字符串

     spliton' (currchar:rest) splitby sofar res | currchar==splitby = spliton' rest splitby "" (res++[sofar]) | otherwise = spliton' rest splitby (sofar++[currchar]) res 

So, to summarize our code: 因此,总结一下我们的代码:

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

Note: This will not however get rid of the empty string - meaning if you have many superfluous spaces - you'll get them added to the list. 注意:但是,这不会摆脱空字符串-这意味着如果您有很多多余的空格-您会将它们添加到列表中。 I'll leave you to think how to handle that case - hope this can help you get started. 我会让您考虑如何处理该案件-希望这可以帮助您入门。

Let's split this up in several sub-problems: 让我们将其分为几个子问题:

  1. Make a list of characters from the string so that we can easily apply pattern matching. 列出字符串中的字符,以便我们可以轻松地应用模式匹配。
  2. Scrape the initial part of the list (as long as possible with only spaces or only not-spaces), and only keep it when it is not whitespace. 刮擦列表的初始部分(尽可能只使用空格或不使用空格),并且仅在不使用空格时保留它。
  3. Repeat the second step while the list is non-empty. 列表为非空时,请重复第二步。

The first thing can be done using fromString . 第一件事可以使用fromString完成。 For the second and third step, we define a helper function: 对于第二步和第三步,我们定义一个辅助函数:

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

The first alternative is the base case to match the empty list. 第一种选择是匹配空列表的基本情况。 The second alternative matches the whole list cs with a first element c . 第二种选择将整个列表cs与第一个元素c相匹配。 If the first character is a space, we recursively (step 3) call the same function on the same list without the initial part of spaces. 如果第一个字符是空格,则我们递归地(第3步)在同一列表上调用相同的函数,而没有空格的起始部分。 If the first character is not a space, we use span :: (a -> Bool) [a] -> ([a], [a]) to split the list in the initial part that is a word, and the rest. 如果第一个字符不是空格,则使用span :: (a -> Bool) [a] -> ([a], [a])将列表拆分成单词的开头部分,其余部分。 We store the word using toString as a string, and recursively call scrape for the rest of the list. 我们使用toString作为字符串存储单词,然后对列表的其余部分递归调用scrape

Now, we only need a wrapper to make this a function with the type String -> [String] : 现在,我们只需要一个包装即可使它成为类型为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

Note that you can easily abstract from the delimiter, by passing a character d and replacing isSpace c with c == d and (not o isSpace) by ((<>) d) . 请注意,您可以通过传递字符d并将isSpace c替换为c == d并将(not o isSpace)((<>) d)来轻松地从定界符中抽象出来。 Alternatively, you can choose to not pass a character d but a function isDelim :: Char -> Bool . 另外,您可以选择不传递字符d而是选择函数isDelim :: Char -> Bool You then get isDelim c and (not o isDelim) , respectively. 然后,分别获得isDelim c(not o isDelim)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM