我有以下作业:
Examples:
split '#' "foo##goo" = ["foo","","goo"]
split '#' "#" = ["",""]
我编写了以下函数:
split :: Char -> String -> [String]
split c "" = [""]
split a "a" = ["",""]
split c st = takeWhile (/=c) st : split c tail((dropWhile (/=c) st))
它不能编译,我不明白为什么。
TakeWhile 将所有不是 c 的字符添加到结果中,然后 tail 删除已经找到的 c,我们递归地将 split 应用于字符串的其余部分,通过 dropWhile 获得。 : 应该制作一个“列表”列表,因为字符串是 Haskell 中的字符列表。我的思想差距在哪里?
更新:
我已将我的程序更新为以下内容:
my_tail :: [a]->[a]
my_tail [] = []
my_tail xs = tail xs
split :: Char -> String -> [String]
split c "" = [""]
split a "a" = ["",""]
split c st = takeWhile (/=c) st ++ split c (my_tail(dropWhile (/=c) st))
我仍然收到错误消息,如下所示:
为什么是预期的类型 [String] 然后是 [Char]?
最佳答案
这不编译的原因是因为 Haskell 将您的最后一个子句视为:
split c st = takeWhile (/=c) st : split c tail ((dropWhile (/=c) st))
因此,它认为您将三个参数应用于
split
: c
、 tail
和 ((dropWhile (/=c) st))
。您应该在这里使用括号,例如:split c st = takeWhile (/=c) st : split c (tail (dropWhile (/=c) st))
但这并不能完全解决问题。例如,如果我们尝试运行您的测试用例,我们会看到:
Prelude> split '#' "foo##goo"
["foo","","goo"*** Exception: Prelude.tail: empty list
tail :: [a] -> [a]
是一个“非总”函数。对于空列表,tail
会出错。确实:Prelude> tail []
*** Exception: Prelude.tail: empty list
最终,列表将用完字符,然后
tail
将引发错误。我们可能想在这里使用 span :: (a -> Bool) -> [a] -> ([a], [a])
,并使用模式匹配来确定是否还有一些元素需要处理,例如:split :: Eq a => a -> [a] -> [[a]]
split _ [] = [[]]
split c txt = pf : rst
where rst | (_:sf1) <- sf = split c sf1
| otherwise = []
(pf,sf) = span (c /=) txt
这里
span (c /=) txt
将因此将非空列表 txt
分成两部分 pf
(前缀)是不等于 c
的项目的最长前缀。 sf
(后缀)是剩下的元素。无论
sf
是否为空,我们都会发出前缀 pf
。然后我们检查后缀。我们知道要么 sf
为空(我们到达了列表的末尾),要么 sf
的第一个元素等于 c
。因此,我们使用模式保护来检查这是否与 (_:sf1)
模式匹配。如果 sf
非空,就会发生这种情况。在这种情况下,我们将 sf1
与 sf
的尾部绑定(bind),然后在尾部进行递归。如果 sf1
为空,我们可以停止,从而返回 []
。例如:
Prelude> split '#' "foo##goo"
["foo","","goo"]
Prelude> split '#' "#"
["",""]
关于list - 在 Haskell 中使用分隔字符将字符串分成更多部分,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/56672750/