问题是将连续重复的列表元素打包到子列表中。
我不明白elem
如何处理单个元素,
例如,
pack [1,1,2,2,3,4]
然后x
将为1,(head (pack xs))
将为1。
怎么能:
1 `elem` 1
elem
类型a -> [a] -> Bool
时的工作?
另外,请解释一下递归。
pack :: (Eq a) => [a] -> [[a]]
pack [] = []
pack [x] = [[x]]
pack (x:xs) = if x `elem` (head (pack xs))
then (x:(head (pack xs))):(tail (pack xs))
else [x]:(pack xs)
答案 0 :(得分:7)
在您的示例中,x
将为1,但head (pack xs)
不是 1.实际上,您可以从其类型签名中看到{{1}的类型对于某种数字pack xs
,必须是[[Int]]
(或至少[[a]]
),即数字列表列表,a
类型为head (pack xs)
,而不是[Int]
类型。无论它的价值是什么,它都不能是Int
。
如果仍然不清楚,请查看较短的示例1
。它会将模式pack [1,2]
与pack (x:xs)
和x=1
匹配,因此xs=[2]
调用的右侧将为elem
。您可以验证这相当于评估为head (pack [2])
的{{1}},现在是以下表达式:
head [[2]]
很有道理。
请记住,即使[2]
返回列表的第一个元素,Haskell也允许列表列表,列表列表的第一个元素是另一个列表。
编辑:要解释递归,让我们详细了解1 `elem` [2]
的完整示例。每当我们尝试评估head
来电时,我们都会使用pack [1,1,2]
中我编号的其中一种模式:
pack
当Haskell尝试评估pack
时,因为pack [] = [] -- #1
pack [x] = [[x]] -- #2
pack (x:xs) = if x `elem` (head (pack xs)) -- #3
then (x:(head (pack xs))):(tail (pack xs))
else [x]:(pack xs)
等同于pack [1,1,2]
(如果您不明白为什么,请查看[1,1,2]
运算符的定义,试试看几个例子!),它将模式#3与1:[1,2]
和:
相匹配。因此,Haskell可以使用模式#3的右侧对x=1
和xs=[1,2]
的这些替换值进行评估。为了完全清楚,具有这些替换的RHS看起来如下,我们将其称为“表达式(A)”:
x
在继续之前,请确保您认为这是正确的!现在,评估这个表达式最困难的部分是弄清楚如何评估xs
,它在几个地方使用。那么,让我们弄清楚Haskell如何评估if 1 `elem` (head (pack [1,2]))
then (1:(head (pack [1,2]))):(tail (pack [1,2]))
else [1]:(pack [1,2])
。同样,因为pack [1,2]
相当于pack [1,2]
(检查它!),所以这会将模式#3与[1,2]
和1:[2]
相匹配。具有这些替换的模式#3的RHS如下,我们将其称为“表达式(B)”:
x=1
要评估表达式(B),我们需要计算出xs=[2]
的值。这次,该表达式将模式#2与if 1 `elem` (head (pack [2]))
then (1:(head (pack [2]))):(tail (pack [2]))
else [1]:(pack [2])
匹配。 (实际上,它也会与模式#3匹配pack [2]
和x=2
,但Haskell使用匹配的第一个模式,因此它从不考虑这种可能性。)将x=2
替换为RHS对于模式#2,我们得到xs=[]
的以下等价值,我们将其称为“表达式(C)”,即使它太短也可能不值得一个名字。
x=2
考虑到这一点并将其替换回上面的表达式(B),我们实际得到:
pack [2]
我在这里完成的所有操作都会被[[2]]
替换为if 1 `elem` (head [[2]])
then (1:(head [[2]])):(tail [[2]])
else [1]:[[2]]
,但是我删除了一些没有改变含义的额外括号。 pack [2]
语句中的条件与[[2]]
相同,后者为false,因此值为if
,可以将其重写为1 `elem` [2]
。 (再次,检查你是否不明白为什么。)这是表达式(B)的最终值,因此是[1]:[[2]]
的最终值。现在,我们可以将此值替换为表达式(A)以获取:
[[1],[2]]
现在,因为pack [1,2]
只是if 1 `elem` (head [[1],[2]])
then (1:(head [[1],[2]])):(tail [[1],[2]])
else [1]:[[1],[2]]
,head [[1],[2]]
语句中的条件是[1]
,这是真的,所以Haskell会评估{{1}给出的部分子句。这很难看,但你应该能够说服自己,它的价值在于:
if
这是表达式(A)的最终值,但当然这是1 `elem` [1]
的值,所以你可以看到它已经完成了。
答案 1 :(得分:1)
正如chi已经评论过的那样,使用head
和tail
通常会产生问题并且模糊不清。在您的示例中,它还会产生巨大的性能问题,因为在每个递归步骤中,您反复评估pack xs
- 除非编译器介入一些非平凡的优化,否则这具有指数复杂性!
我建议使用case
和警卫:
pack (x:xs) = case pack xs of
thisPack : packs
| x `elem` thisPack -> (x:thisPack) : packs
otherPacks -> [x] : otherPacks