head.reverse与last的空间复杂度

时间:2013-03-17 22:25:16

标签: haskell space-complexity

在许多系统中,head.reverse需要与列表大小成比例的空间,而last需要恒定的空间。

是否有系统可以执行此类转换?同样适用于reverse.take n.reverse

编辑:我想扩展一下我的问题:我不是经过一次具体的转变 - 我宁愿在任何优化之后。

3 个答案:

答案 0 :(得分:5)

您可以通过将列表视为特别迟钝的自然数来转换reverse . take n . reverse:空列表为零,并且conses为succ。对于编码为列表的惰性自然,减法为drop

type LazyNat a = [a]

lengthLazy :: [a] -> LazyNat a
lengthLazy = id

dropLazy :: LazyNat a -> [b] -> [b]
dropLazy [] xs = xs
dropLazy (_:n) (_:xs) = dropLazy n xs
dropLazy _ _ = []

-- like Prelude.subtract, this is flip (-)
subtractLazy :: Int -> LazyNat a -> LazyNat a
subtractLazy = drop

现在我们可以轻松实现“采取最后n”功能:

takeLast n xs = dropLazy (subtractLazy n (lengthLazy xs)) xs

...你会很高兴地知道在任何特定时间只需要n个记忆。特别是,takeLast 1(或任何文字takeLast N的{​​{1}})可以在常量内存中运行。您可以通过比较运行N时发生的情况与在ghci中运行takeLast 5 [1..]时发生的情况进行比较来验证这一点。

当然,我尝试使用上面非常具有暗示性的名称,但在实际实现中,您可能会在上面列出所有废话:

(reverse . take 5 . reverse) [1..]

答案 1 :(得分:1)

您可以为此编写一个简单的重写规则。

http://www.haskell.org/haskellwiki/Playing_by_the_rules

融合规则也可能会捕获它,具体取决于反向编码方式。

答案 2 :(得分:1)

如果我们比较drop和last

>>> last [1..10^5]
100000
(0.01 secs, 10496736 bytes)
>>> last [1..10^6]
1000000
(0.05 secs, 81968856 bytes)
>>> last [1..10^7]
10000000
(0.32 secs, 802137928 bytes)


>>> drop (10^5-1) [1..10^5]
[100000]
(0.01 secs, 10483312 bytes)
>>> drop (10^6-1) [1..10^6]
[1000000]
(0.05 secs, 82525384 bytes)
>>> drop (10^7-1) [1..10^7]
[10000000]
(0.32 secs, 802142096 bytes)

我们在空间和时间上获得类似的表现,我必须承认我有点欺骗,因为在这里我们不需要计算列表的长度。无论如何,我相信它不应该是太空问题。然后你的反向。拿n。可以使用drop和length表示反向


作为旁注,我已经测试了其他解决方法,结果很糟糕。

takeLastN = foldl' (.) id . flip replicate tail 

lastN = foldl' (.) last . flip replicate init