您的位置:首頁技術文章
文章詳情頁

JavaScript中的惰性數組介紹

瀏覽:3日期:2023-11-13 10:06:52

今天我要介紹的是 lazy-arr ,它給JavaScript中帶來了惰性數組。

什么是惰性數組,它為什么有用?

我們來重現一下你第一次面試軟件工程師時的題目:寫一個斐波納契函數。我們明確了0和1的基本情況,然后遞歸生成剩下的:

let fib = n => { switch (n) { case 0: return 0 case 1: return 1 default: return fib(n - 1) + fib(n - 2) }}

小菜一碟。

這種解決方案有什么問題嗎?還用說么 - 效率真的真的很低。要計算第n個斐波那契數字,我們要調用 fib 函數 2的n-1冪次!簡直糟糕的要命,我們應該做的更好一點。

一種方法是記錄 fib 的輸出。就是說,由于 fib 是純粹和冪等的,我們可以緩存其輸出:

let memoize = fn => { let cache = new Map return _ => { if (!cache.has(_)) { cache.set(_, fn(_)) } return cache.get(_) }}let fib = memoize(n => { switch (n) { case 0: return 0 case 1: return 1 default: return fib(n - 1) + fib(n - 2) }})

好多了!現在我們輸入為n時,只需調用 fib n - 1次。

我們還能怎么表達這種思想呢?

懶序列

在Scale中,你可以這樣做(這得歸功于 Philipp Gabler ):

def fib(n: Int): Stream[Int] = { lazy val stream: Stream[Int] = 0 #:: stream.scan(1)(_ + _) stream.take(n)}

上面做的事情就是定義一個惰性的數據流(兩個初始數字,加上一個能產生更多數字的函數),當調用 fib(n) 時,它返回第n個數字,或者如果還沒有計算,就生成并返回它。另一種思考方式是用生成器和一個記錄先前產生值的緩存,這樣之后就可以通過值的索引進行訪問。

惰性流是一個非常酷的抽象概念,對于計算開銷昂貴的序列,或者是不可能計算所有索引的序列都有效(即:無限序列)。這在功能性語言中很受歡迎,特別是默認具有惰性求值的語言。比如在Haskell中就是這樣做:

fibs :: [Integer]fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

同樣的思維,但在Clojure就是這樣:

(defn fib [] ((fn rfib [a b](cons a (lazy-seq (rfib b (+ a b))))) 0 1))

你大概明白了吧。

那在JavaScript中怎么用呢?

JavaScript中的惰性序列

通過 lazy-arr ,你可以像這樣:

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

就這樣!然后,您可以根據需要訪問數組中的項,并根據需要進行計算:

fibs[10] // 55fibs[7] // 13

第一行計算第10個斐波納契數,并且由于我們遞歸地進行計算的定義(按照以前的斐波那契數),我們需要計算前9個斐波那契數,以此來計算第10個。所以當我們在第二行計算第七個斐波納契數時,結果會立即返回,因為我們已經計算好了!

最重要的是,就用戶關心的來說, fibs 數組并不會做任何懈怠地、有狀態地或遞歸地或其他類似的事情。它只是一個數組。那些麻煩的東西被lazy-arr封裝好了,生成器就是一行代碼。

是不是很優雅?

來自:http://www.zcfy.cc/article/performancejs-introducing-lazy-arrays-in-javascript-3312.html

標簽: JavaScript
相關文章:
国产综合久久一区二区三区