繁体   English   中英

Haskell函数参数力评估

[英]Haskell function parameter force evaluation

我需要从列表中获取最后n元素,使用O(n)内存,所以我编写了这段代码

take' :: Int -> [Int] -> [Int]
take' n xs = (helper $! (length $! xs) - n + 1) xs
    where helper skip [] = []
          helper skip (x : xs) = if skip == 0 then xs else (helper $! skip - 1) xs

main = print (take' 10 [1 .. 100000])

这段代码需要O(|L|)内存,其中|L| - 是给定列表的长度。

但是当我写这段代码时

take' :: Int -> [Int] -> [Int]
take' n xs = helper  (100000 - n + 1) xs
    where helper skip [] = []
          helper skip (x : xs) = if skip == 0 then xs else (helper $! skip - 1) xs


main = print (take' 10 [1 .. 100000])

这段代码现在只需要O(n)内存(唯一的chage是(helper $! (length $! xs) - n + 1) - > helper (100000 - n + 1)

因此,据我所知,Haskell由于某种原因在第一次调用helper之前没有评估length xs ,所以它在skip留下了一个thunk并且haskell必须在每个堆栈帧中保留这个值而不是进行尾递归。 但是在第二段代码中,它会评估(100000 - n + 1)并为helper提供纯值。

所以问题是如何在第一次调用helper之前评估列表的长度,并且只使用O(n)内存。

另一个答案提到成为一个好消费者意味着什么。 您已经发布了两个版本的函数,一个适用于任意长度的列表,但不是一个好的消费者,一个是一个很好的消费者,但假定一个特定的列表长度。 为了完整性,这里有一个功能很好的消费者,适用于任意列表长度:

takeLast n xs = go (drop n xs) xs where
    go (_:xs) (_:ys) = go xs ys
    go _ ys = ys

第二个版本并不真正只占用On )内存。 无论take'什么take' :你从一个长度为L的列表开始,并且必须存储在某个地方。

有效占用On )内存的原因是该列表仅由一个“好消费者”使用,即helper 这样的消费者从头到尾解构清单; 因为在其他任何地方都不需要对头部的引用,垃圾收集器可以立即开始清理那些第一个元素 - 在列表理解之前甚至已经建立了列表的其余部分!

但是,如果在使用helper之前,您会计算该列表的length 这已经迫使整个列表成为NF'd ,正如我所说,这不可避免地需要OL )内存。 因为您仍然持有与helper一起使用的引用,所以在这种情况下,垃圾收集器在整个列表在内存之前不能执行任何操作。

所以,它与严格的评估无关。 实际上,实现目标的唯一方法是使其不那么严格(只需要在任何给定时间评估长度为n的子列表)。


更确切地说:它强制列表的脊椎为正常形式。 不评估元素,但它仍然是OL )。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM