Haskell:模式匹配以组合String

时间:2013-10-18 16:07:13

标签: haskell pattern-matching

我正在尝试编写一个函数,将字符串中的单个字符添加到字符串列表中,例如

combine ", !" ["Hello", "", "..."] = ["Hello,", " ", "...!"]

我试过这个:

combine :: String -> [String] -> [String]
combine (y:ys) (x:xs) =
[x:y, combine ys xs]

3 个答案:

答案 0 :(得分:2)

如果要逐个元素地组合列表,通常是您正在查看的zip。在这种情况下,您确切地知道如何组合元素 - 这使它成为zipWith

zipWith采用“组合函数”,然后创建一个使用所述组合函数组合两个列表的函数。让我们调用你的“组合”函数append,因为它会在字符串的末尾添加一个字符。您可以这样定义:

append char string = string ++ [char]

你看到它是如何工作的吗?例如,

append 'e' "nic" = "nice"

append '!' "Hello" = "Hello!"

现在我们知道了,回想一下zipWith采用“组合函数”,然后创建一个使用该函数组合两个列表的函数。因此,您的功能可以轻松实现为

combine = zipWith append

并且在您提供的列表中按顺序对每个元素执行append,如下所示:

combine ", !" ["Hello", "", "..."] = ["Hello,", " ", "...!"]

答案 1 :(得分:2)

一个简单的就是

 combine :: [Char] -> [String] -> [String]
 combine [] _ = []
 combine _ [] = []
 combine (c:cs) (x:xs) = x ++ [c] : combine cs xs

甚至更简单地使用zipWith

 combine :: [Char] -> [String] -> [String]
 combine = zipWith (\c x -> x ++ [c])

我必须做一些额外的工作才能让它发挥作用。我会为你分解它。

首先,我将函数的类型指定为[Char] -> [String] -> [String]。我可以使用String作为第一个参数,但是你在概念上操作的是字符列表和字符串列表,而不是字符串和字符串列表。

接下来,我必须为此函数指定边缘情况。当任一参数为空列表[]时会发生什么?简单的答案就是结束计算,所以我们可以编写

combine [] _ = []
combine _ [] = []

此处_匹配任何内容,但将其丢弃,因为它未在返回值中使用。

接下来,对于函数的实际主体我们想要获取第一个字符和第一个字符串,然后将该字符附加到字符串的末尾:

combine (c:cs) (x:xs) = x ++ [c]

但是这对csxs,我们列表的其余部分没有任何作用(甚至不会使用上面的类型签名进行编译)。我们需要继续前进,因为我们正在生成一个列表,所以通常使用前置运算符:

来完成
combine (c:cs) (x:xs) = x ++ [c] : combine cs xs

然而,这是一种常见的模式,有一个名为zipWith的辅助函数可以为我们处理边缘情况。它的类型签名是

zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]

它同时向下走两个输入列表,将相应的元素传递给提供的函数。由于我们要应用的函数是\c x -> x ++ [c](变成lambda函数),我们可以将它放到zipWith作为

combine cs xs = zipWith (\c x -> x ++ [c]) cs xs

但是Haskell会让我们在可能的情况下放弃参数,所以我们可以将其减少到

combine :: [Char] -> [String] -> [String]
combine = zipWith (\c x -> x ++ [c])

就是这样!

答案 2 :(得分:0)

你很亲密。你有什么问题。

y的类型为Char,x的类型为String,它是[Char]的别名。这意味着您可以使用y:x将y添加到列表顶部,但不能使用相同的:运算符将y添加到列表的末尾。相反,您将y放入列表并加入列表。

x ++ [y]

还必须有一个基本案例,否则这个递归将继续,直到它在列表和崩溃中都没有元素。在这种情况下,我们可能没有任何想要添加的内容。

combine [] [] = []

最后,一旦我们创建了元素y ++ [x],我们就想将它添加到我们计算的其余项目的顶部。因此,我们使用:将其列入我们的列表。

combine :: String -> [String] -> [String]
combine [] [] = []
combine (x : xs) (y : ys) = (y ++ [x]) : (combine xs ys)

关于此代码的一个注意事项,如果您的字符串中的字符数与列表中的字符串数不同,那么这将会崩溃。你可以通过多种方式处理这种情况,bheklilr的答案解决了这个问题。

kqr的答案也很有效,可能是在实践中最好的答案。