[英]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: 您基本上想模拟对列表的迭代,所以这是我的建议:
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]
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.
当我们的字符串为空时,我们只想返回到目前为止保存的内容。
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: 让我们将其分为几个子问题:
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.