Haskell(:)和(++)的差异

时间:2009-11-30 04:36:23

标签: list haskell syntax

我很抱歉这样的问题。我不太确定Haskell中:++运算符的区别。

x:y:[] = [x,y]  

[x] ++ [y] = [x,y]

对于我这个问题的反向函数,

reverse ::[a]->[a]
reverse [] = []
reverse (x:xs) = reverse(xs)++[x]

为什么以下不起作用?

reversex ::[Int]->[Int]
reversex [] = []
reversex (x:xs) = reversex(xs):x:[]

给出类型错误。

5 个答案:

答案 0 :(得分:67)

:运算符称为“cons”运算符,用于将head元素添加到列表中。因此[]是一个列表,x:[]正在将x添加到列表[x]的空列表中。如果您继续y:[x],则最终会得到与[y, x]相同的列表y:x:[]

++运算符是列表并置运算符,它将两个列表作为操作数并将它们“组合”到一个列表中。因此,如果您有列表[x]和列表[y],那么您可以将它们连接起来:[x]++[y]以获取[x, y]。

请注意,:采用一个元素和一个列表,而++采用两个列表。

至于你的代码不起作用。

reversex ::[Int]->[Int]
reversex [] = []
reversex (x:xs) = reversex(xs):x:[]

反向函数评估为列表。由于:运算符未将列表作为其第一个参数,因此reverse(xs):x无效。但reverse(xs)++[x]有效。

答案 1 :(得分:20)

:将一个元素合并到一个列表中。 ++附加两个列表。

前者有类型

a -> [a] -> [a]

而后者有类型

[a] -> [a] -> [a]

答案 2 :(得分:11)

与(++)

连接

也许我正在考虑深入研究这个问题,但是, 据我所知,如果你试图连接 使用(++)列出例如:

[1, 2, 3] ++ [4, 5]

(++)必须遍历完整的左侧列表。 看看code of (++)就可以了 更清楚了。

(++) :: [a] -> [a] -> [a]
(++) []     ys = ys
(++) (x:xs) ys = x : xs ++ ys

因此,由于每次通话都需要避免使用(++) reverse(xs)++[x]列表越来越大(或者更小,取决于 在观点上。无论如何,该程序只需要遍历另一个程序 列出每个电话)

示例:

假设我通过连接实现了反向。

reversex ::[Int]->[Int]
reversex [] = []
reversex (x:xs) = reversex(xs)++[x]

反转列表[1,2,3,4]看起来有点像这样:

reversex [1, 2, 3, 4]
reversex [2, 3, 4]               ++ [1]
reversex [3, 4]           ++ [2] ++ [1]
reversex [4]       ++ [3] ++ [2] ++ [1]
reversex [] ++ [4] ++ [3] ++ [2] ++ [1]
         [] ++ [4] ++ [3] ++ [2] ++ [1]
         [4]       ++ [3] ++ [2] ++ [1]
         [4, 3]           ++ [2] ++ [1]
         [4, 3, 2]               ++ [1]
         [4, 3, 2, 1]

使用cons运算符的尾递归(:)!!!

处理调用堆栈的一种方法是添加accumulator。 (并不总是可以只添加一个累加器。但大部分都是 一个处理的递归函数是primitive recursive,因此可以 转化为tail recursive functions。)

在累加器的帮助下,可以制作这个例子 使用cons运算符(:)工作。 累加器 - 我的示例中的ys - 累积当前结果并作为参数传递。由于蓄能器,我们现在能够 使用 cons 运算符通过追加来累积结果 每次我们的初始名单的负责人。

reverse' :: (Ord a) => [a] -> [a] -> [a]
reverse' (x:xs) ys = reverse' xs (x:ys)
reverse' [] ys     = ys

这里有一点需要注意。

累加器是一个额外的参数。我不知道Haskell 提供默认参数,但在这种情况下它会很好, 因为你总是用空列表调用这个函数 像这样的累加器:reverse' [1, 2, 3, 4] []

有很多关于尾递归的文献和我 确定有很多类似的问题 StackExchange / StackOverflow 。如果您发现任何错误,请纠正我。

亲切的问候,

编辑1

Ness是否会为那些感兴趣的人指出一些非常好的答案链接:

编辑2

确定。 感谢dFeuer和他的更正,我想我理解Haskell 好一点。

1. $!超出了我的理解范围。在我的所有测试中,似乎 让事情变得更糟。

2.正如dFeuer指出: 表示(:)xy的应用程序的thunk在语义上与x:y相同,但占用的内存更多。所以这对cons运营商来说是特殊的 (和懒惰的构造函数)并没有必要以任何方式强迫事物。

3.如果我使用非常相似的函数sumUp列表的整数, 通过BangPatterns或seq函数进行严格评估 如果使用得当,将防止堆栈增长过大。 e.g:

sumUp' :: (Num a, Ord a) => [a] -> a -> a
sumUp' (x:xs) !y = reverse' xs (x + y)
sumUp' [] y      = y

注意y前面的爆炸声。我在ghci和它中尝试过 占用更少的内存。

答案 3 :(得分:3)

cons往往是一个类型构造函数而不是运算符。这里的示例:可以在let..in..表达中使用,但++不是

let x : xs = [1, 2, 3] in x -- known as type deconstructing

将返回1但

let [x] ++ [y, z] = [1, 2, 3] in x

将返回错误Variable not in scope x

为了方便起见,请考虑cons这样的

data List a = Cons a (List a) -- is equvalent with `data [a] = a:[a]`

https://en.wikibooks.org/wiki/Haskell/Other_data_structures

此外,如果您想使用cons反转数组。这是一个例子,知识来自Prolog

import Data.Function

reversex1 [] = []
reversex1 arr = reversex arr []

reversex [] arr = arr
reversex (x:xs) ys = reversex xs (x:ys)

main = do
    reversex1 [1..10] & print

答案 4 :(得分:0)

您可以稍作更改以获得正确的结果。

reversex ::[Int]->[Int] # comment this line
reversex [] = []
reversex (x:xs) = reversex(xs) ++ x : []