摘要:有這樣的說法,并非柯里化有什么意義,而是,當(dāng)函數(shù)可以作為函數(shù)的參數(shù)和返回值,成為函數(shù)式編程語言后,就會(huì)不可避免地產(chǎn)生函數(shù)柯里化。函數(shù)柯里化允許和鼓勵(lì)你分隔復(fù)雜功能變成更小更容易分析的部分。那么用函數(shù)柯里化就能實(shí)現(xiàn)提前返回。
#### 前言
在計(jì)算機(jī)科學(xué)中,柯里化(英語:Currying),又譯為卡瑞化或加里化,是把接受多個(gè)參數(shù)的函數(shù)變換成接受一個(gè)單一參數(shù)(最初函數(shù)的第一個(gè)參數(shù))的函數(shù),并且返回接受余下的參數(shù)而且返回結(jié)果的新函數(shù)的技術(shù)。
#### 一、為什么會(huì)有函數(shù)柯里化?
Currying 的重要意義在于可以把函數(shù)完全變成「接受一個(gè)參數(shù);返回一個(gè)值」的固定形式,這樣對(duì)于討論和優(yōu)化會(huì)更加方便。
將關(guān)注的重點(diǎn)聚焦到函數(shù)本身,而不因冗余的數(shù)據(jù)參數(shù)分散注意力。
有這樣的說法,并非柯里化有什么意義,而是,當(dāng)函數(shù)可以作為函數(shù)的參數(shù)和返回值,成為函數(shù)式編程語言后,就會(huì)不可避免地產(chǎn)生函數(shù)柯里化。
#### 二、具體實(shí)現(xiàn)
先來一個(gè)簡(jiǎn)單的 add 函數(shù)
function add(x, y) { return x + y; } add(2, 3); // 5
重要的概念多說一遍:函數(shù)柯里化就是接收多個(gè)參數(shù)的函數(shù)變換為接收一個(gè)函數(shù),并返回接收余下參數(shù),最終能返回結(jié)果的技術(shù)
。
那么,繼續(xù):
function add(x) { return function(y) { return x + y; } } add(2)(3); // 5
所以,曾經(jīng)的一個(gè)函數(shù),因?yàn)殚]包操作(返回函數(shù)并訪問了自由變量的行為),變成了多個(gè)接收一個(gè)參數(shù)的函數(shù)。
所以簡(jiǎn)單來講:函數(shù)柯里化就是意圖將函數(shù)的參數(shù)變成一個(gè)。讓函數(shù)可以輸入一個(gè)值,就返回一個(gè)相對(duì)應(yīng)的值,從而實(shí)現(xiàn)純函數(shù)化。
為什么函數(shù)式編程要求函數(shù)必須是純的,不能有副作用?因?yàn)樗且环N數(shù)學(xué)運(yùn)算,原始目的就是求值,不做其他事情,否則就無法滿足函數(shù)運(yùn)算法則了。在函數(shù)式編程中,函數(shù)就是一個(gè)管道(pipe)。這頭進(jìn)去一個(gè)值,那頭就會(huì)出來一個(gè)新的值,沒有其他作用。
所以良好的編程規(guī)范是盡可能讓函數(shù)塊做一個(gè)事情,實(shí)現(xiàn)可復(fù)用性,可維護(hù)性。
上面的例子中,如果有很多個(gè)參數(shù)怎么辦,難道一層層嵌套?
我們繼續(xù):
function plus(value) { "use strict"; var add = function () { var args = []; var adder = function adder() { Array.prototype.push.apply(args,Array.prototype.slice.apply(arguments)) return adder; } adder.toString = function () { return args.reduce(function(a, b) { return a + b; }) } return adder; } return add()(value); } plus(2)(3)(5).toString(); // 10;
上面的代碼看起來不那么優(yōu)雅,如果是減法,我們就得又重新為減法寫這么多的代碼。像 lodash, underscore 這些工具庫,都提供了柯里化的工具函數(shù)。
我們一起來試著實(shí)現(xiàn):
function curry(fn, args) { var length = fn.length; // 函數(shù)參數(shù)的長(zhǎng)度 // 閉包保存參數(shù)列表 args = args || []; return function() { // 獲取參數(shù)列表。 var _args = args.slice(0); Array.prototype.push.apply(_args, Array.prototype.slice.call(arguments)) if (_args.length < length) { // 如果傳入的參數(shù)列表長(zhǎng)度還沒有超過函數(shù)定義時(shí)的參數(shù)長(zhǎng)度,就 push 新的參數(shù)到參數(shù)列表中保存起來。 // 自己調(diào)用自己,將保存的參數(shù)傳遞到下一個(gè)柯里化函數(shù)。 return curry.call(this, fn, _args); } else { // 如果傳入的參數(shù)列表長(zhǎng)度已經(jīng)超過函數(shù)定義時(shí)的參數(shù)長(zhǎng)度,就執(zhí)行。 return fn.apply(this, _args); } } }三、應(yīng)用場(chǎng)景
函數(shù)柯里化的好處有幾個(gè):
參數(shù)復(fù)用;
提前返回;
延遲計(jì)算/運(yùn)行。
函數(shù)柯里化允許和鼓勵(lì)你分隔復(fù)雜功能變成更小更容易分析的部分。這些小的邏輯單元顯然是更容易理解和測(cè)試的,然后你的應(yīng)用就會(huì)變成干凈而整潔的組合,由一些小單元組成的組合。
文章開篇的 add 函數(shù),假如,每次調(diào)用加法有一個(gè)初始值會(huì)怎樣?
var add = curry(function(a, b, c) { return a + b + c; }) var addTen = add(10); var addSix = add(6); addTen(2)(3); // 15; addSix(7)(8); // 21;
以上代碼就實(shí)現(xiàn)了參數(shù)復(fù)用,保存固定參數(shù)的函數(shù)。
看一個(gè)經(jīng)典的例子:
元素綁定事件監(jiān)聽器:
var addEvent = function(el, type, fn, capture) { if (window.addEventListener) { el.addEventListener(type, function(e) { fn.call(el, e); }, capture); } else if (window.attachEvent) { el.attachEvent("on" + type, function(e) { fn.call(el, e); }); } };
以上代碼是為了兼容 IE 瀏覽器對(duì) DOM 事件綁定做的函數(shù)封裝。
問題在于,每次對(duì) DOM 元素進(jìn)行事件綁定時(shí),函數(shù)內(nèi)部都會(huì)走一遍 if else。那么用函數(shù)柯里化就能實(shí)現(xiàn)提前返回。
var addEvent = (function(){ if (window.addEventListener) { return function(el, sType, fn, capture) { el.addEventListener(sType, function(e) { fn.call(el, e); }, (capture)); }; } else if (window.attachEvent) { return function(el, sType, fn, capture) { el.attachEvent("on" + sType, function(e) { fn.call(el, e); }); }; } })();總結(jié)
函數(shù)柯里化是“函數(shù)是一等公民”的編程語言環(huán)境形成的編程風(fēng)格,利用了函數(shù)能作為參數(shù)一級(jí)返回值以及利用了閉包保存變量的特點(diǎn),是將多個(gè)參數(shù)的函數(shù)轉(zhuǎn)換為接收一個(gè)參數(shù),最后返回結(jié)果的技術(shù)。
歡迎關(guān)注我的個(gè)人公眾號(hào)“謝南波”,專注分享原創(chuàng)文章。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/101449.html
摘要:里也有柯里化的實(shí)現(xiàn),只是平時(shí)沒有在意。如果函數(shù)柯里化后雖然生搬硬套,不過現(xiàn)實(shí)業(yè)務(wù)也會(huì)有類似場(chǎng)景。 柯里化 先解釋下什么是 柯里化 在計(jì)算機(jī)科學(xué)中,柯里化(英語:Currying),又譯為卡瑞化或加里化,是把接受多個(gè)參數(shù)的函數(shù)變換成接受一個(gè)單一參數(shù)(最初函數(shù)的第一個(gè)參數(shù))的函數(shù),并且返回接受余下的參數(shù)而且返回結(jié)果的新函數(shù)的技術(shù)。 js 里也有柯里化的實(shí)現(xiàn),只是平時(shí)沒有在意。先把原文簡(jiǎn)介貼...
摘要:專題系列共計(jì)篇,主要研究日常開發(fā)中一些功能點(diǎn)的實(shí)現(xiàn),比如防抖節(jié)流去重類型判斷拷貝最值扁平柯里遞歸亂序排序等,特點(diǎn)是研究專題之函數(shù)組合專題系列第十六篇,講解函數(shù)組合,并且使用柯里化和函數(shù)組合實(shí)現(xiàn)模式需求我們需要寫一個(gè)函數(shù),輸入,返回。 JavaScript 專題之從零實(shí)現(xiàn) jQuery 的 extend JavaScritp 專題系列第七篇,講解如何從零實(shí)現(xiàn)一個(gè) jQuery 的 ext...
摘要:一個(gè)經(jīng)常會(huì)看到的函數(shù)的實(shí)現(xiàn)為第一版我們可以這樣使用或者或者已經(jīng)有柯里化的感覺了,但是還沒有達(dá)到要求,不過我們可以把這個(gè)函數(shù)用作輔助函數(shù),幫助我們寫真正的函數(shù)。 JavaScript 專題系列第十三篇,講解函數(shù)柯里化以及如何實(shí)現(xiàn)一個(gè) curry 函數(shù) 定義 維基百科中對(duì)柯里化 (Currying) 的定義為: In mathematics and computer science, cu...
摘要:專題系列第十四篇,講解偏函數(shù)以及如何實(shí)現(xiàn)一個(gè)函數(shù)定義維基百科中對(duì)偏函數(shù)的定義為翻譯成中文在計(jì)算機(jī)科學(xué)中,局部應(yīng)用是指固定一個(gè)函數(shù)的一些參數(shù),然后產(chǎn)生另一個(gè)更小元的函數(shù)。 JavaScript 專題系列第十四篇,講解偏函數(shù)以及如何實(shí)現(xiàn)一個(gè) partial 函數(shù) 定義 維基百科中對(duì)偏函數(shù) (Partial application) 的定義為: In computer science, pa...
摘要:專題系列第十六篇,講解函數(shù)組合,并且使用柯里化和函數(shù)組合實(shí)現(xiàn)模式需求我們需要寫一個(gè)函數(shù),輸入,返回。這便是函數(shù)組合。 JavaScript 專題系列第十六篇,講解函數(shù)組合,并且使用柯里化和函數(shù)組合實(shí)現(xiàn) pointfree 模式 需求 我們需要寫一個(gè)函數(shù),輸入 kevin,返回 HELLO, KEVIN。 嘗試 var toUpperCase = function(x) { return...
閱讀 3753·2021-10-13 09:39
閱讀 3804·2021-09-24 09:48
閱讀 1203·2021-09-01 10:30
閱讀 2533·2019-08-30 15:55
閱讀 1786·2019-08-29 16:39
閱讀 2304·2019-08-26 13:55
閱讀 3057·2019-08-26 12:23
閱讀 1643·2019-08-26 11:59