JavaScript惰性求值的一種實(shí)現(xiàn)方法示例
前言
在學(xué)習(xí) Haskell 時(shí),我遇到了這種寫(xiě)法:
sum (takeWhile (<10000) (filter odd (map (^2) [1..])))
這段代碼的意思是,找出自然整數(shù)中小于 10000 的同時(shí)是乘方數(shù)和奇數(shù)的數(shù)字,再把這些數(shù)加總。由于 Haskell 的懶運(yùn)算特性,上面的程序并不會(huì)立馬生成從 1 到 無(wú)限大的自然數(shù)列表,而是會(huì)等待 takeWhile 指令,再生成符合條件的列表。如果用 JS 來(lái)寫(xiě),很難寫(xiě)出這么簡(jiǎn)潔高表達(dá)性的代碼。一個(gè)可能的思路就是寫(xiě)個(gè) while 循環(huán),然后找到符合條件的數(shù)進(jìn)行加總。這個(gè)比較簡(jiǎn)單,我就不演示了。
但是如果我們要用高階函數(shù)來(lái)模擬 Haskell 的寫(xiě)法,就要想個(gè)辦法實(shí)現(xiàn)懶運(yùn)算了。提到懶,首先想到的就是 Iterator 。沒(méi)人踢它一腳告訴它 next(),它會(huì)一直坐那兒不動(dòng)的。
現(xiàn)在我們就來(lái)用 Iterator 來(lái)實(shí)現(xiàn)一個(gè)懶運(yùn)算。
首先定義一個(gè)生成從 1 到無(wú)窮大自然數(shù)的 generator :
const numbers = function*() { let i = 1 while (true) { yield i++ } }
由于只有在 generator 執(zhí)行后生成的 iterable 上執(zhí)行 next() 方法,yield 才會(huì)執(zhí)行,所以我們要做的主要工作就是實(shí)現(xiàn)不同的 next 方法,達(dá)到目的。
我們需要先創(chuàng)建一個(gè)工廠函數(shù) Lazy,Lazy 封裝了我們的各種目標(biāo)操作 :
const Lazy = iterator => { const next = iterable.next.bind(iterable) const map = () => {} const filter = () => {} const takeWhile = () => {} return { next, map, filter, takeWhile, }
我們先實(shí)現(xiàn) map 方法,它會(huì)把每次 next 返回的值根據(jù)提供的回調(diào)函數(shù)進(jìn)行修改:
const map = f => { const modifiedNext = () => { const item = next() const mappedValue = f(item.value) return { value: mappedValue, done: item.done, } } const newIter = { ...iterable, next: modifiedNext } return lazy(newIter) }
再定義 filter 方法,它會(huì)讓 next 只返回符合判斷條件的值:
const filter = predicate => { const modifiedNext = () => { while (true) { const item = next() if (predicate(item.value)) { return item } } } const newIter = { ...iterable, next: modifiedNext } return lazy(newIter) }
最后,定義 takeWhile,它會(huì)限制 next 執(zhí)行的條件,一旦條件不滿足,則停止執(zhí)行 next 并返回歷史執(zhí)行結(jié)果:
const takeWhile = predicate => { const result = [] let value = next().value while (predicate(value)) { result.push(value) value = next().value } return result }
主要的方法都定義完了,現(xiàn)在把它們合并起來(lái):
const Lazy = iterable => { const next = iterable.next.bind(iterable) const map = f => { const modifiedNext = () => { const item = next() const mappedValue = f(item.value) return { value: mappedValue, done: item.done, } } const newIter = { ...iterable, next: modifiedNext } return lazy(newIter) } const filter = predicate => { const modifiedNext = () => { while (true) { const item = next() if (predicate(item.value)) { return item } } } const newIter = { ...iterable, next: modifiedNext } return lazy(newIter) } const takeWhile = predicate => { const result = [] let value = next().value while (predicate(value)) { result.push(value) value = next().value } return result } return Object.freeze({ map, filter, takeWhile, next, }) } const numbers = function*() { let i = 1 while (true) { yield i++ } }
現(xiàn)在用我們寫(xiě)的 Lazy 和 numbers 函數(shù)來(lái)實(shí)現(xiàn)文章開(kāi)頭的 Haskell 代碼:
Lazy(numbers()) .map(x => x ** 2) .filter(x => x % 2 === 1) .takeWhile(x => x < 10000) .reduce((x, y) => x + y) // => 16650
參考:
Lazy Evaluation in JavaScript with Generators, Map, Filter, and Reduce
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
JS基礎(chǔ)之邏輯結(jié)構(gòu)與循環(huán)操作示例
這篇文章主要介紹了JS基礎(chǔ)之邏輯結(jié)構(gòu)與循環(huán)操作,結(jié)合實(shí)例形式分析了JavaScript邏輯判斷、流程控制、循環(huán)語(yǔ)句等相關(guān)操作技巧,需要的朋友可以參考下2020-01-01javascritp添加url參數(shù)將參數(shù)加入到url中
javascritp添加url參數(shù)方法,將參數(shù)加入到url中,如果原來(lái)url中有則覆蓋,下面是示例代碼,感興趣的朋友可以參考下2014-09-09基于bootstrap頁(yè)面渲染的問(wèn)題解決方法
今天小編就為大家分享一篇基于bootstrap頁(yè)面渲染的問(wèn)題解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-08-08Javascript計(jì)算兩個(gè)marker之間的距離(Google Map V3)
做地圖開(kāi)發(fā),最常用到的就是marker一些操作和交互。簡(jiǎn)單介紹一下,兩個(gè)marker之間的距離計(jì)算,感興趣的朋友可以參考下哈,希望對(duì)你有所幫助2013-04-04jquery的$getjson調(diào)用并獲取遠(yuǎn)程的JSON字符串問(wèn)題
jQuery中常用getJSON來(lái)調(diào)用并獲取遠(yuǎn)程的JSON字符串,將其轉(zhuǎn)換為JSON對(duì)象,如果成功,則執(zhí)行回調(diào)函數(shù),本文將詳細(xì)介紹,需要的朋友可以參考下2012-12-12使用Web?Component實(shí)現(xiàn)防篡改水印
Web?Component內(nèi)部有鉤子天然支持被篡改時(shí)被觸發(fā),用來(lái)防篡改非常方便,所以本文就將使用Web?Component實(shí)現(xiàn)防篡改水印,感興趣的小伙伴可以了解下2023-12-12