Haskell函数使用列表和zip的输出不正确

时间:2019-04-07 19:15:50

标签: list haskell

我的代码应该采用字符串,例如ccccchhrrre并返回成对列表[('c', 5) , ('h', 2) , ('r', 3), ('e', 1)]。该对的第一部分是字母,第二部分是重复直到下一个字符串出现的次数。

但是,当前,我的代码返回[('c',11)]-这是字符串中的第一个字符和字符总数的长度:

> fun4 "ccccchhrrre"
[('c',11)]

我的问题是fun4。运行 func3

> fun3 "ccccchhrrre"
["ccccc","hh","rrr","e"]
fun3 :: String -> [String]
fun3 "" = [] 
fun3 xs = fun2 xs : fun3 (drop(length(fun2 xs))xs)

func4 :: String -> [(Char, Int)]
func4 fun3 = zip (fun3)[length fun3]

我感谢提供详细说明的任何帮助。

2 个答案:

答案 0 :(得分:4)

有两个问题。首先是,当您编写以下内容时:

func4 :: String -> [(Char, Int)]
func4 fun3 = zip (fun3)[length fun3]

此定义中的fun3与您先前定义的fun3函数完全没有关联。如果您打开了编译器警告(例如,使用标志-Wall),则会警告您在此fun3定义中,func4的绑定会“阴影”现有的绑定。这意味着它的使用方式与您使用完整的单独名称相同:

func4 :: String -> [(Char, Int)]
func4 bob = zip (bob) [length bob]

因此,当您评估时:

func4 "ccccchhrrre"

根本不使用函数fun3。相反,它通过func4的定义扩展为:

func4 "ccccchhrrre"
= zip "ccccchhrrre" [length "ccccchhrrre"]
= zip "ccccchhrrre" [11]

并且由于Haskell中的字符串是字符列表,因此与以下内容相同:

= zip ['c','c',...] [11]

当您压缩两个列表并且一个列表比另一个列表短时,当列表用完时,zip结束,因此产生:

= [('c', 11)]

,并忽略字符列表的第二个和后续元素。

您可能想要的是编写func4以获取字符串参数并将其传递给fun3

func4 :: String -> [(Char, Int)]
func4 xs = zip (fun3 xs) [length (fun3 xs)]

不幸的是,这不会输入check。问题是,这等效于:

func4 "ccccchhrrre"
= zip ["ccccc","hh","rrr","e"] [length ["ccccc","hh","rrr","e"]]
= zip ["ccccc","hh","rrr","e"] [4]
= [("ccccc", 4)]

其类型为[(String, Char)],而不是您期望的[(Char, Char)]

如果您了解了map,可能会认为了解以下信息很有用:

> map head ["ccccc","hh","rrr","e"]
"chre"
>

"chre"等同于列表['c','h','r','e']。如果您能以某种方式获得长度列表[5,2,3,1]的方式与获得头部列表的方式相同,那么这些列表将非常有用。

如果您不想使用map,可能会发现编写func4来处理fun3 output 很有帮助,就像这样:

> func4 ["ccccc","hh","rrr","e"]
[('c',5),('h',2),('r',3),('e',1)]
>

签名会稍有不同,但结构与您的fun3类似:

func4 :: [String] -> [(Char, Int)]
func4 [] = []
func4 (str : strs) = ??? : func4 strs

,然后您的最终函数可以将fun3func4链接在一起:

rle :: String -> [(Char, Int)]
rle str = func4 (fun3 str)

答案 1 :(得分:0)

other answer为您的代码中的问题提供了很好的解释。这是一个略有不同的解决方案,我发现它更容易理解。

fun3可以替换为现有功能Data.List.groupgroup获取一个列表并生成一个列表列表,其中包含相等的相邻元素:

> Data.List.group "ccccchhrrre"
["ccccc","hh","rrr","e"]

使用fun4也可以简化map应该执行的工作。匿名函数\x -> (head x, length x)接受一个参数x,并生成一个元组,其第一个元素为x的头,第二个元素为x的长度。将此函数映射到group的输出上应该可以为我们提供所需的结果:

> map (\x -> (head x, length x)) ["ccccc","hh","rrr","e"]
[('c',5),('h',2),('r',3),('e',1)]

我们现在可以将group和我们的map合并为一个函数,并为其指定一个更合适的名称:

-- Count adjacent repeated characters
countRep :: String -> [(Char, Int)]
countRep str = map (\x -> (head x, length x)) (List.group str)

只是为了娱乐

我们编写的函数可在可以比较其项是否相等的任何列表上使用!这里是无点样式:

countRep :: Eq a => [a] -> [(a, Int)]
countRep = map (liftA2 (,) head length) . List.group