我正在努力学习haskell我坚持以下问题
记下函数split :: RawText - > [Word]分割给定的字符串 到一个单词列表。
为了简单起见,我们假设原始文本只是一串字母和空格,可能有不规则的间距。 例如:split"一两三ff" = ["一个","两个","三个"," ff"]
我一直在["一个" ***例外:recursion.hs:30:1-14:功能溢出的非详尽模式'所以递归永远不会结束,但我有一个基本情况。
我的功能
\dontrun{}
答案 0 :(得分:6)
您声明了一个函数split'
和一个函数spilt'
。纠正错别字,你应该好好去!
split' :: RawText -> [SingleWord]
spilt' [] = [] -- the first typo is here
split' xs
| xs == [] = []
| isBlank (head xs) = split' xs
| otherwise = waitForBlank xs : spilt' (tail xs) -- here is the second typo
要解释该消息,最好将代码拆分为您声明的两个不同函数:
split' :: RawText -> [SingleWord]
split' xs
| xs == [] = []
| isBlank (head xs) = split' xs
| otherwise = waitForBlank xs : spilt' (tail xs)
-- spilt' :: [a] -> [a] (or something like this)
spilt' [] = []
现在,当您在split'
递送时,请致电spilt'
。由于您声明的函数spilt'
没有处理非空列表,因此会抛出异常。
另外请注意,如果你纠正错字,你不必两次处理空列表:
import Prelude hiding (split)
split :: RawText -> [SingleWord]
split [] = []
split xs
-- | xs == [] = [] this case is matched by the pattern
-- above and thus not needed
| isBlank (head xs) = split' xs
| otherwise = waitForBlank xs : split (tail xs)
此外,当列表上的模式匹配时,您可以通过明确写出第head
个应用程序明确地模式匹配列表的tail
和cons
:
split s@(x:xs)
| isBlank x = split' xs
| otherwise = waitForBlank s : split' xs
但不知何故,这个功能似乎仍然很笨重,必须有一个更好的方法来跳过所有字母。让我们看看库函数,看看有什么可以帮助我们。你可以找到它们here:
-- drops letters from RawText while a certain predicate holds
dropWhile :: (a -> Bool) -> [a] -> [a]
-- takes letters form RawText while a certain predicate holds
takeWhile :: (a -> Bool) -> [a] -> [a]
这看起来很有希望。现在我们可以将waitForBlank
重写为:
waitForBlank xs = takeWhile (not . isBlank) xs
-- or, pointfree:
waitForBlank = takeWhile (not . isBlank)
使用dropWhile
我们还可以通过跳过所有非空格字符来提高split
功能的效率。请注意,该空格包含在dropWhile
的部分中,必须明确删除。
split :: RawText -> [SingleWord]
split [] = []
split xs = waitForBlank xs : split (dropUntilBlank xs)
-- With dropUntilBlank defined as
dropUntilBlank xs = tail (dropWhile (not . isBlank) xs)
-- without the call of tail, dropUntilBlank would keep the space in between the words:
dropWhile (not . isBlank) "a word" => " word"
-- note the extra space: ^^^
-- using tail on this now produces the correct word:
tail (dropWhile (not . isBlank) "a word") = "word"
现在结果看起来很干净:
split :: RawText -> [SingleWord]
split [] = []
split xs = waitForBlank xs : split (dropUntilBlank xs)
waitForBlank xs = takeWhile (not . isBlank) xs
dropUntilBlank xs = tail (dropWhile (not . isBlank) xs)
最后一部分也可以使用break
:
split :: RawText -> [SingleWord]
split [] = []
split xs = word : split' rest
where
(word, (space:rest)) = break isBlank xs
这个答案假设所有单词都由一个空格分隔。对于多个空格,必须通过将dropUntilBlank
替换为tail
来重写dropWhile isBlank
以排除多个空格。
答案 1 :(得分:0)
我的解决方案(欢迎反馈,新手在这里)
split' :: RawText -> [SingleWord]
split' [] = []
split' (x:xs)
| isBlank x = split' xs
| otherwise = waitForBlank (x:xs) : split' (drop (length (waitForBlank (x:xs))) xs)
isBlank :: Char -> Bool
isBlank x = if x == ' ' then True else False
waitForBlank :: String -> String
waitForBlank [] = []
waitForBlank (x:xs)
| isBlank x = []
| otherwise = x : waitForBlank xs