JavaScript中的惰性数组介绍

什么是惰性数组,它为什么有用?

我们来重现一下你第一次面试软件工程师时的题目:写一个斐波纳契函数。我们明确了0和1的基本情况,然后递归生成剩下的:

let fib = n => {

小菜一碟。

这种解决方案有什么问题吗?还用说么 - 效率真的真的很低。要计算第n个斐波那契数字,我们要调用fib函数 2的n-1幂次!简直糟糕的要命,我们应该做的更好一点。

一种方法是记录fib的输出。就是说,由于fib是纯粹和幂等的,我们可以缓存其输出:

let memoize = fn => {

好多了!现在我们输入为n时,只需调用fib n - 1次。

我们还能怎么表达这种思想呢?

懒序列

在Scale中,你可以这样做(这得归功于Philipp Gabler):

def fib(n: Int): Stream[Int] = {

上面做的事情就是定义一个惰性的数据流(两个初始数字,加上一个能产生更多数字的函数),当调用fib(n)时,它返回第n个数字,或者如果还没有计算,就生成并返回它。另一种思考方式是用生成器和一个记录先前产生值的缓存,这样之后就可以通过值的索引进行访问。

惰性流是一个非常酷的抽象概念,对于计算开销昂贵的序列,或者是不可能计算所有索引的序列都有效(即:无限序列)。这在功能性语言中很受欢迎,特别是默认具有惰性求值的语言。比如在Haskell中就是这样做:

fibs :: [Integer]

同样的思维,但在Clojure就是这样:

(defn fib []

你大概明白了吧。

那在JavaScript中怎么用呢?

JavaScript中的惰性序列

通过lazy-arr,你可以像这样:

let fibs = lazy([0, 1])(_ => fibs[_ - 1] + fibs[_ - 2])

就这样!然后,您可以根据需要访问数组中的项,并根据需要进行计算:

fibs[10] // 55

第一行计算第10个斐波纳契数,并且由于我们递归地进行计算的定义(按照以前的斐波那契数),我们需要计算前9个斐波那契数,以此来计算第10个。所以当我们在第二行计算第七个斐波纳契数时,结果会立即返回,因为我们已经计算好了!

最重要的是,就用户关心的来说,fibs数组并不会做任何懈怠地、有状态地或递归地或其他类似的事情。它只是一个数组。那些麻烦的东西被lazy-arr封装好了,生成器就是一行代码。

  • 我的微信
  • 这是我的微信扫一扫
  • weinxin
  • 我的微信公众号
  • 我的微信公众号扫一扫
  • weinxin
avatar

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: