我知道Haskell没有循环,所以我不能这样做。我也知道递归在这里是“有用的”,但这就是我所知道的全部。 到目前为止,我已经获得了一个基本的类型签名,即
toSplit :: String -> [String]
基本上,它从字符串变成单词列表...
谢谢!
P.S。我想使用takeWhile
和dropWhile
函数...而不是库...
答案 0 :(得分:3)
如果您想自己实现,那么要做的第一件事就是找到第一个单词。 您可以执行以下操作:
takeWhile (/=' ') s
如果字符串以定界字符开头,则需要先修剪它们。我们可以使用dropWhile
来做到这一点。
takeWhile (/=' ') $ dropWhile (==' ') s
现在我们有了第一个单词,但是我们还需要从第一个单词开始的字符串的其余部分,然后在该字符串上递归。我们可以使用splitAt
:
(_, rest) = splitAt (length word) s
然后我们使用字符串的rest
进行递归,并将第一个单词限制在该递归的结果上,从而为我们提供所有单词的列表。
我们需要定义一个基本情况,即空字符串的结果,一旦没有更多字符,该结果将终止递归。
toSplit :: String -> [String]
toSplit "" = []
toSplit s =
let word = takeWhile (/=' ') $ dropWhile (==' ') s
(_, rest) = splitAt (length word) s
in word : toSplit (dropWhile (==' ') rest)
编辑:上面的代码中有一个错误,它不能正确处理边缘情况。
错误是它在原始s
上调用splitAt,但是如果s
有前导空格,则会给出错误的结果:
*Main> toSplit " foo"
["foo","o"]
这应该修复该错误:
let trimmed = dropWhile (==' ') s
word = takeWhile (/=' ') trimmed
(_, rest) = splitAt (length word) trimmed
剩下一个极端的情况:
*Main> toSplit " "
[""]
一种可能的解决方案是使用辅助函数:
toSplit :: String -> [String]
toSplit = splitWords . dropWhile (==' ')
where
splitWords "" = []
splitWords s =
let word = takeWhile (/=' ') s
(_, rest) = splitAt (length word) s
in word : splitWords (dropWhile (==' ') rest)
答案 1 :(得分:1)
我知道Haskell没有循环,所以我不能这样做。我也知道 递归在这里“很有帮助” ...
是,Haskell使用递归而不是命令式语言中的for
,while
循环结构。
代替我自己编写递归函数,更常见的用法是map
,foldr (foldl)
,unfoldr
等递归遍历列表。这些功能的优点是您可以将要执行的操作集中在step
函数中。像您的问题一样,适合使用unfoldr
来完成它。
以下是takeWhile
和dropWhile
的示例:
import Data.Char (isSpace)
import Data.List (unfoldr)
toSplit::String->[String]
toSplit = unfoldr step
where step [] = Nothing
step xs = Just (takeWhile (not . isSpace) xs,
(dropWhile isSpace . dropWhile (not . isSpace)) xs)
但是有点冗长,使用break
函数可能更易读,例如:
toSplit' = unfoldr step
where step [] = Nothing
step xs = let (word, rest) = break isSpace xs
in Just (word, dropWhile isSpace rest)