我很抱歉这样的问题。我不太确定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:[]
给出类型错误。
答案 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指出:
表示(:)
到x
和y
的应用程序的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 : []