摘要:舉個(gè)例子,如果我們實(shí)現(xiàn)一個(gè)三個(gè)數(shù)的加法函數(shù),需要這么實(shí)現(xiàn)如果我們將其柯里化變換成接受一個(gè)單一參數(shù)的函數(shù),并且返回接受余下的參數(shù)而且返回結(jié)果的新函數(shù),我們的調(diào)用方式應(yīng)該是這樣的。
什么是柯里化僅以此文獻(xiàn)給我的學(xué)弟 誅諾_彌 ,并將逐風(fēng)者的祝福送給他:
英雄,愿你有一份無悔的愛情!
維基百科中有如下定義:
在計(jì)算機(jī)科學(xué)中,柯里化(英語:Currying),是把接受多個(gè)參數(shù)的函數(shù)變換成接受一個(gè)單一參數(shù)(最初函數(shù)的第一個(gè)參數(shù))的函數(shù),并且返回接受余下的參數(shù)而且返回結(jié)果的新函數(shù)的技術(shù)。
舉個(gè)例子,如果我們實(shí)現(xiàn)一個(gè)三個(gè)數(shù)的加法函數(shù),需要這么實(shí)現(xiàn):
function add(a, b, c) { return a + b + c; } add(1, 2, 3); // 6
如果我們將其柯里化(變換成接受一個(gè)單一參數(shù)的函數(shù),并且返回接受余下的參數(shù)而且返回結(jié)果的新函數(shù)),我們的調(diào)用方式應(yīng)該是這樣的。
add(1)(2)(3); // 6
注意到在接受最后一個(gè)參數(shù)前,柯里化后的函數(shù)返回值都是函數(shù),因此我們實(shí)現(xiàn)如下:
function add(a) { return function (b) { return function (c) { return a + b + c; } } } add(1)(2)(3); // 6
這樣我們就實(shí)現(xiàn)了一個(gè)柯里化的add函數(shù)。由于ES6中引入了箭頭函數(shù),我們可以將上面的add實(shí)現(xiàn)成這樣:
const add = a => b => c => a + b + c;
我想你大概知道為什么ES6要引入箭頭函數(shù)了。
柯里化的用途就目前我們知道的來看,柯里化僅僅是修改了一下函數(shù)參數(shù)的傳入方式或者說函數(shù)的調(diào)用方式,那么有什么用呢?
考慮下面這個(gè)求兩數(shù)相除余數(shù)的函數(shù):
const modulo = divisor => dividend => dividend % divisor; modulo(3)(9); // 0
有個(gè)這個(gè)函數(shù),我們現(xiàn)在能夠很輕松的寫出判斷一個(gè)數(shù)是奇數(shù)還是偶數(shù)的函數(shù):
const isOdd = modulo(2); isOdd(6); // 0 isOdd(5); // 1
如果你沒有布爾值一定要用true、false的強(qiáng)迫癥的話,這是一個(gè)很不錯(cuò)的方案:)
接下來我們來實(shí)現(xiàn)下面這個(gè)需求:給定一個(gè)由數(shù)字構(gòu)成的數(shù)組,獲取里面所有的奇數(shù),怎么辦呢?
先準(zhǔn)備一個(gè)filter函數(shù):
const filter = condition => arr => arr.filter(condition);
然后實(shí)現(xiàn)我們的getTheOdd:
const getTheOdd = filter(isOdd); getTheOdd([1, 2, 3, 4, 5]); // [1, 3, 5]
到這里我說一下我的理解,柯里化的函數(shù)有這樣一種能力:組合。將簡單的函數(shù)組合起來實(shí)現(xiàn)更復(fù)雜的功能,一方面能夠更好的復(fù)用你的代碼,另一方面能夠培養(yǎng)一種對代碼拆解的直覺。
就像我們在實(shí)現(xiàn)函數(shù)節(jié)流的時(shí)候(比如onscroll事件處理函數(shù)頻繁調(diào)用問題這樣的場景),常常會(huì)使用throttle包一下處理函數(shù)一樣,拆解代碼并進(jìn)行組合往往能給我們帶來更多的價(jià)值。
接下來我們來看這個(gè)例子:
// 該函數(shù)接收一個(gè)數(shù)組,返回該數(shù)組元素倒序后的數(shù)組 const reverse = arr => arr.slice().reverse(); // 該函數(shù)接收一個(gè)數(shù)組,返回?cái)?shù)組的第一個(gè)元素 const first = arr => arr[0]; // 基于上面兩個(gè)函數(shù)我們可以輕松實(shí)現(xiàn)獲取數(shù)組最后一個(gè)元素的函數(shù) const last = arr => { const reversed = reverse(arr); return first(arr); };
在提供函數(shù)式編程能力的JavaScript庫中,通常都會(huì)有一個(gè)用于組合的實(shí)現(xiàn)組合的函數(shù):compose。我們可以用它來讓前一個(gè)例子更加函數(shù)式:
const compose = (...funcs) => { if (funcs.length === 0) { return arg => arg; } if (funcs.length === 1) { return funcs[0]; } return funcs.reduce((a, b) => (...args) => a(b(...args))); }; const last = compose(first, reverse);從組合到傳播
考慮以下場景,我們有一個(gè)執(zhí)行很慢的函數(shù)(記為slowFunc),我們希望能夠?qū)λ闹颠M(jìn)行緩存,以此提高性能,怎么辦呢?相信很多人都會(huì)想到memoize函數(shù),我們在下面給一個(gè)相對簡潔的實(shí)現(xiàn),參考:https://github.com/reactjs/re...
const slowFuncWithCache = memoize(slowFunc); function memoize(fn) { let lastArgs = null; let lastResult = null; return (...args) => { if (!isAllArgsEqual(args, lastArgs)) { lastArgs = args; lastResult = fn(...args); } return lastResult; }; } function isAllArgsEqual(prev, next) { if (prev === null || next === null || prev.length !== next.length) { return false; } for (let i = 0; i < prev.length; i++) { if (prev[i] !== next[i]) { return false; } } return true; }
回看我們前面介紹compose時(shí)使用的例子。
const reverse = arr => arr.slice().reverse(); const first = arr => arr[0]; const last = compose(first, reverse);
如果我們現(xiàn)在有一個(gè)需求,需要給last加一個(gè)緩存,怎么辦?你可能直接就想到了這樣:
const last = memoize(compose(first, reverse));
不過其實(shí)我們還可以這樣:
const last = compose(memoize(first), memoize(reverse));
在last內(nèi)層的first和reverse,在經(jīng)過memoize處理獲得緩存的能力后,也讓last獲得了緩存的能力。這就是組合的傳播。
絕不覺得這很熟悉?讓我們回顧一下小學(xué)數(shù)學(xué)的知識(shí):
a * (b + c) = a * b + a * c
組合的這種傳播特性給了我們一種新的思路:如果我們要實(shí)現(xiàn)一個(gè)大系統(tǒng)的數(shù)據(jù)緩存功能,不妨試著將系統(tǒng)中的每一步計(jì)算都加上緩存進(jìn)行處理,如果每一步都進(jìn)行了計(jì)算上的緩存,那么最終這個(gè)系統(tǒng)一定是帶有緩存能力的。
結(jié)語從認(rèn)識(shí)柯里化,到利用柯里化的能力去組合我們的更復(fù)雜的邏輯,再到把內(nèi)部組合的出功能傳播到外層,這是一種化繁為簡,以簡解繁的方法。在此借用otakustay前輩的一句話:嘗試始終將你的邏輯拆解到最簡,藉由組合和傳播,你會(huì)獲得更多的可能性。
參考鏈接Hey Underscore, You"re Doing It Wrong!
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/89447.html
摘要:三使用場景場景性能優(yōu)化可以將一些模板代碼通過柯里化的形式預(yù)先定義好,例如這段代碼的作用就是根據(jù)瀏覽器的類型決定事件添加的方式。場景擴(kuò)展能力中的方法,就是通過柯里化實(shí)現(xiàn)的四總結(jié)通過本文的介紹,相信你對柯里化已經(jīng)有一個(gè)全新的認(rèn)識(shí)了。 歡迎關(guān)注我的公眾號睿Talk,獲取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、前言 柯里化...
摘要:函數(shù)式接口之前在上看到一個(gè)技術(shù)視頻,講的接口為什么不好用,以及什么樣的接口更好用。演講者是的作者,他提出了一種全面函數(shù)式的接口設(shè)計(jì)模式。言歸正傳,今天聊聊這樣的接口如何實(shí)現(xiàn),以及中的模塊。 函數(shù)式 js 接口 之前在 youtube 上看到一個(gè)技術(shù)視頻,講underscore.js的接口為什么不好用,以及什么樣的接口更好用。演講者是 lodash.js 的作者,他提出了一種全面函數(shù)式的...
摘要:引言上一節(jié)介紹了高階函數(shù)的定義,并結(jié)合實(shí)例說明了使用高階函數(shù)和不使用高階函數(shù)的情況。我們期望函數(shù)輸出,但是實(shí)際上調(diào)用柯里化函數(shù)時(shí),所以調(diào)用時(shí)就已經(jīng)執(zhí)行并輸出了,而不是理想中的返回閉包函數(shù),所以后續(xù)調(diào)用將會(huì)報(bào)錯(cuò)。引言 上一節(jié)介紹了高階函數(shù)的定義,并結(jié)合實(shí)例說明了使用高階函數(shù)和不使用高階函數(shù)的情況。后面幾部分將結(jié)合實(shí)際應(yīng)用場景介紹高階函數(shù)的應(yīng)用,本節(jié)先來聊聊函數(shù)柯里化,通過介紹其定義、比較常見的...
摘要:碰到這種面試官,你只有是個(gè)題霸,再加上眼緣夠才能順利入圍。只要按照我題目的思路,甚至打出來測試用例看看,就能實(shí)現(xiàn)這個(gè)題目了。答案根據(jù)的,對答案做出修正。另我的答案絕不敢稱最佳,隨時(shí)歡迎優(yōu)化修正。但了解總歸是好的。 我們在長期的面試過程中,經(jīng)歷了種種苦不堪言,不訴苦感覺不過癮(我盡量控制),然后主要聊聊常見JavaScript面試題的解法,以及面試注意事項(xiàng) 憶苦 面試第一苦,面試官的土 ...
摘要:寫在前面專題系列是我寫的第二個(gè)系列,第一個(gè)系列是深入系列。專題系列自月日發(fā)布第一篇文章,到月日發(fā)布最后一篇,感謝各位朋友的收藏點(diǎn)贊,鼓勵(lì)指正。 寫在前面 JavaScript 專題系列是我寫的第二個(gè)系列,第一個(gè)系列是 JavaScript 深入系列。 JavaScript 專題系列共計(jì) 20 篇,主要研究日常開發(fā)中一些功能點(diǎn)的實(shí)現(xiàn),比如防抖、節(jié)流、去重、類型判斷、拷貝、最值、扁平、柯里...
閱讀 2793·2021-09-23 11:44
閱讀 1681·2021-09-13 10:24
閱讀 2629·2021-09-08 09:36
閱讀 1238·2019-08-30 15:54
閱讀 2258·2019-08-30 13:54
閱讀 3317·2019-08-30 10:57
閱讀 1856·2019-08-29 18:43
閱讀 3622·2019-08-29 15:10