Haskell递归函数,它将一串单词拆分为单独的字符串

时间:2016-05-21 11:52:36

标签: haskell

我正在努力学习haskell我坚持以下问题

记下函数split :: RawText - > [Word]分割给定的字符串 到一个单词列表。

为了简单起见,我们假设原始文本只是一串字母和空格,可能有不规则的间距。 例如:split"一两三ff" = ["一个","两个","三个"," ff"]

我一直在["一个" ***例外:recursion.hs:30:1-14:功能溢出的非详尽模式'所以递归永远不会结束,但我有一个基本情况。

我的功能

\dontrun{}

2 个答案:

答案 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个应用程序明确地模式匹配列表的tailcons

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