与 (++) 的连接
也许我正在考虑深入研究,但是,
据我了解,如果您尝试连接
例如使用(++) 的列表:
[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:
Will Ness 为感兴趣的人指出了一些指向非常好的答案的链接:
编辑 2:
好的。
感谢 dFeuer 和他的更正,我想我了解 Haskell
好一点。
1.$! 超出我的理解范围。在我所有的测试中,它似乎
让事情变得更糟。
2.正如 dFeuer 指出的:
代表(:) 应用到x 和y 的thunk 在语义上与x:y 相同,但占用更多内存。所以这对于 cons 运算符来说是特殊的
(和懒惰的构造函数),没有必要以任何方式强制执行。
3.如果我使用非常相似的函数来对列表的整数求和,
通过 BangPatterns 或 seq 函数进行严格评估
如果使用得当,将防止堆栈变得太大。例如:
sumUp' :: (Num a, Ord a) => [a] -> a -> a
sumUp' (x:xs) !y = reverse' xs (x + y)
sumUp' [] y = y
注意 y 前面的爆炸。我在ghci中试过了
占用更少的内存。