摘要:在函數(shù)式編程中數(shù)據(jù)在由純函數(shù)組成的管道中傳遞。函數(shù)式編程中函子是實(shí)現(xiàn)了函數(shù)的容器下文中將函子視為范疇,模型可表示如下但是在函數(shù)式編程中要避免使用這種面向?qū)ο蟮木幊谭绞饺《畬ν獗┞读艘粋€(gè)的接口也稱為。
該系列會有 3 篇文章,分別介紹什么是函數(shù)式編程、剖析函數(shù)式編程庫、以及函數(shù)式編程在 React 中的應(yīng)用,歡迎關(guān)注我的 blog
命令式編程和聲明式編程拿泡茶這個(gè)事例進(jìn)行區(qū)分命令式編程和聲明式編程
命令式編程
1.燒開水(為第一人稱)
2.拿個(gè)茶杯
3.放茶葉
4.沖水
聲明式編程
1.給我泡杯茶(為第二人稱)
舉個(gè) demo
// 命令式編程 const convert = function(arr) { const result = [] for (let i = 0; i < arr.length; i++) { result[i] = arr[i].toLowerCase() } return result } // 聲明式編程 const convert = function(arr) { return arr.map(r => r.toLowerCase()) }什么是函數(shù)式編程
函數(shù)式編程是聲明式編程的范式。在函數(shù)式編程中數(shù)據(jù)在由純函數(shù)組成的管道中傳遞。
函數(shù)式編程可以用簡單如交換律、結(jié)合律、分配律的數(shù)學(xué)之法來幫我們簡化代碼的實(shí)現(xiàn)。
它具有如下一些特性:
純粹性: 純函數(shù)不改變除當(dāng)前作用域以外的值;
// 反面示例 let a = 0 const add = (b) => a = a + b // 兩次 add(1) 結(jié)果不一致 // 正確示例 const add = (a, b) => a + b
數(shù)據(jù)不可變性: Immutable
// 反面示例 const arr = [1, 2] const arrAdd = (value) => { arr.push(value) return arr } arrAdd(3) // [1, 2, 3] arrAdd(3) // [1, 2, 3, 3] // 正面示例 const arr = [1, 2] const arrAdd = (value) => { return arr.concat(value) } arrAdd(3) // [1, 2, 3] arrAdd(3) // [1, 2, 3]
在后記 1 中對數(shù)組字符串方法是否對原值有影響作了整理
函數(shù)柯里化: 將多個(gè)入?yún)⒌暮瘮?shù)轉(zhuǎn)化為一個(gè)入?yún)⒌暮瘮?shù);
const add = a => b => c => a + b + c add(1)(2)(3)
偏函數(shù): 將多個(gè)入?yún)⒌暮瘮?shù)轉(zhuǎn)化成兩部分;
const add = a => (b, c) => a + b + c add(1)(2, 3)
可組合: 函數(shù)之間能組合使用
const add = (x) => x + x const mult = (x) => x * x const addAndMult = (x) => add(mult(x))柯里化(curry)
如下是一個(gè)加法函數(shù):
var add = (a, b, c) => a + b + c add(1, 2, 3) // 6
假如有這樣一個(gè) curry 函數(shù), 用其包裝 add 函數(shù)后返回一個(gè)新的函數(shù) curryAdd, 我們可以將參數(shù) a、b 進(jìn)行分開傳遞進(jìn)行調(diào)用。
var curryAdd = curry(add) // 以下輸出結(jié)果都相同 curryAdd(1, 2, 3) // 6 curryAdd(1, 2)(3) // 6 curryAdd(1)(2)(3) // 6 curryAdd(1)(2, 3) // 6動手實(shí)現(xiàn)一個(gè) curry 函數(shù)
核心思路: 若傳進(jìn)去的參數(shù)個(gè)數(shù)未達(dá)到 curryAdd 的個(gè)數(shù),則將參數(shù)緩存在閉包變量 lists 中:
function curry(fn, ...args) { const length = fn.length let lists = args || [] let listLen return function (..._args) { lists = [...lists, ..._args] listLen = lists.length if (listLen < length) { const that = lists lists = [] return curry(fn, ...that) } else if (listLen === length) { const that = lists lists = [] return fn.apply(this, that) } } }代碼組合(compose)
現(xiàn)在有 toUpperCase、reverse、head 三個(gè)函數(shù), 分別如下:
var toUpperCase = (str) => str.toUpperCase() var reverse = (arr) => arr.reverse() var head = (arr) => arr[0]
接著使用它們實(shí)現(xiàn)將數(shù)組末位元素大寫化輸出, 可以這樣做:
var reverseHeadUpperCase = (arr) => toUpperCase(head(reverse(arr))) reverseHeadUpperCase(["apple", "banana", "peach"]) // "PEACH"
此時(shí)在構(gòu)建 reverseHeadUpperCase 函數(shù)的時(shí)候, 必須手動聲明傳入?yún)?shù) arr, 是否能提供一個(gè) compose 函數(shù)讓使用者更加友好的使用呢? 類似如下形式:
var reverseHeadUpperCase = compose(toUpperCase, head, reverse) reverseHeadUpperCase(["apple", "banana", "peach"]) // "PEACH"
此外 compose 函數(shù)符合結(jié)合律, 我們可以這樣子使用:
compose(compose(toUpperCase, head), reverse) compose(toUpperCase, compose(head, reverse))
以上兩種寫法與 compose(toUpperCase, head, reverse) 的效果完全相同, 都是依次從右到左執(zhí)行傳參中的函數(shù)。
此外 compose 和 map 一起使用時(shí)也有相關(guān)的結(jié)合律, 以下兩種寫法效果相等
compose(map(f), map(g)) map(compose(f, g))動手實(shí)現(xiàn)一個(gè) compose 函數(shù)
代碼精華集中在一行之內(nèi), 其為眾多開源庫(比如 Redux) 所采用。
var compose = (...args) => (initValue) => args.reduceRight((a, c) => c(a), initValue)范疇論
范疇論是數(shù)學(xué)中的一個(gè)分支。可以將范疇理解為一個(gè)容器, 把原來對值的操作,現(xiàn)轉(zhuǎn)為對容器的操作。如下圖:
學(xué)習(xí)函數(shù)式編程就是學(xué)習(xí)各種函子的過程。
函數(shù)式編程中, 函子(Functor) 是實(shí)現(xiàn)了 map 函數(shù)的容器, 下文中將函子視為范疇,模型可表示如下:
class Functor { constructor(value) { this.value = value } map(fn) { return new Functor(fn(this.value)) } }
但是在函數(shù)式編程中, 要避免使用 new 這種面向?qū)ο蟮木幊谭绞? 取而代之對外暴露了一個(gè) of 的接口, 也稱為 pointed functor。
Functor.of = value => new Functor(value)Maybe 函子
Maybe 函子是為了解決 this.value 為 null 的情形, 用法如下:
Maybe.of(null).map(r => r.toUpperCase()) // null Maybe.of("m").map(r => r.toUpperCase()) // Maybe?{value: "M"}
實(shí)現(xiàn)代碼如下:
class Maybe { constructor(value) { this.value = value } map(fn) { return this.value ? new Maybe(fn(this.value)) : null } } Maybe.of = value => new Maybe(value)Either 函子
Either 函子 是為了對應(yīng) if...else... 的語法, 即非左即右。因此可以將之拆分為 Left 和 Right 兩個(gè)函子, 它們的用法如下:
Left.of(1).map(r => r + 1) // Left?{value: 1} Right.of(1).map(r => r + 1) // Right?{value: 2}
Left 函子實(shí)現(xiàn)代碼如下:
class Left { constructor(value) { this.value = value } map(fn) { return this } } Left.of = value => new Left(value)
Right 函子實(shí)現(xiàn)代碼如下(其實(shí)就是上面的 Functor):
class Right { constructor(value) { this.value = value } map(fn) { return new Right(fn(this.value)) } } Right.of = value => new Right(value)
具體 Either 函數(shù)只是對調(diào)用 Left 函子 或 Right 函子 作一層篩選, 其接收 f、g 兩個(gè)函數(shù)以及一個(gè)函子(Left or Right)
var Either = function(f, g, functor) { switch(functor.constructor) { case "Left": return f(functor.value) case "Right": return g(functor.value) default: return f(functor.value) } }
使用 demo:
Either((v) => console.log("left", v), (v) => console.log("def", v), left) // left 1 Either((v) => console.log("rigth", v), (v) => console.log("def", v), rigth) // rigth 2Monad 函子
函子會發(fā)生嵌套, 比如下面這樣:
Functor.of(Functor.of(1)) // Functor { value: Functor { value: 1 } }
Monad 函子 對外暴露了 join 和 flatmap 接口, 調(diào)用者從而可以扁平化嵌套的函子。
class Monad { constructor(value) { this.value = value } map(fn) { return new Monad(fn(this.value)) } join() { return this.value } flatmap(fn) { return this.map(fn).join() } } Monad.of = value => new Monad(value)
使用方法:
// join Monad.of(Monad.of(1).join()) // Monad { value: 1 } Monad.of(Monad.of(1)).join() // Monad { value: 1 } // flatmap Monad.of(1).flatmap(r => r + 1) // 2
Monad 函子可以運(yùn)用在 I/O 這種不純的操作上將之變?yōu)榧兒瘮?shù)的操作,目前比較懵懂,日后補(bǔ)充。后記 1: 數(shù)組字符串方法小結(jié)(是否對原值有影響) 不會對原數(shù)組有影響的方法
var test = [1, 2, 3] var result = test.slice(0, 1) console.log(test) // [1, 2, 3] console.log(result) // [1]
var test = [1, 2, 3] var result = test.concat(4) console.log(test) // [1, 2, 3] console.log(result) // [1, 2, 3, 4]對原數(shù)組有影響的方法
var test = [1, 2, 3] var result = test.splice(0, 1) console.log(test) // [2, 3] console.log(result) // [1]
var arr = [2, 1, 3, 4] arr.sort((r1, r2) => (r1 - r2)) console.log(arr) // [1, 2, 3, 4]
var test = [1, 2, 3] var result = test.reverse() console.log(test) // [3, 2, 1] console.log(result) // [3, 2, 1]
var test = [1, 2, 3] var result = test.push(4) console.log(test) // [1, 2, 3, 4] console.log(result) // 4不會對原字符串造成影響的方法
// substr var test = "abc" var result = test.substr(0, 1) console.log(test) // "abc" console.log(result) // a // substring var test = "abc" var result = test.substring(0, 1) console.log(test) // "abc" console.log(result) // a // slice var test = "abc" var result = test.slice(0, 1) console.log(test) // "abc" console.log(result) // a參考
mostly-adequate-guide
JavaScript 專題之函數(shù)柯里化
函數(shù)式編程入門教程
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/101527.html
摘要:第一節(jié)函數(shù)式范式什么是函數(shù)式編程函數(shù)式編程英語或稱函數(shù)程序設(shè)計(jì),又稱泛函編程,是一種編程范型,它將電腦運(yùn)算視為數(shù)學(xué)上的函數(shù)計(jì)算,并且避免使用程序狀態(tài)以及易變對象。 第一節(jié) 函數(shù)式范式 1. 什么是函數(shù)式編程 函數(shù)式編程(英語:functional programming)或稱函數(shù)程序設(shè)計(jì),又稱泛函編程,是一種編程范型,它將電腦運(yùn)算視為數(shù)學(xué)上的函數(shù)計(jì)算,并且避免使用程序狀態(tài)以及易變對...
摘要:它大致概述并討論了前端工程的實(shí)踐如何學(xué)習(xí)它,以及在年實(shí)踐時(shí)使用什么工具。目的是每年發(fā)布一次內(nèi)容更新。前端實(shí)踐第一部分廣泛描述了前端工程的實(shí)踐。對大多數(shù)人來說,函數(shù)式編程看起來更加自然。 1 Front-End Developer Handbook 2017 地址:https://frontendmasters.com/b... 這是任何人都可以用來了解前端開發(fā)實(shí)踐的指南。它大致概述并...
摘要:它大致概述并討論了前端工程的實(shí)踐如何學(xué)習(xí)它,以及在年實(shí)踐時(shí)使用什么工具。目的是每年發(fā)布一次內(nèi)容更新。前端實(shí)踐第一部分廣泛描述了前端工程的實(shí)踐。對大多數(shù)人來說,函數(shù)式編程看起來更加自然。 1 Front-End Developer Handbook 2017 地址:https://frontendmasters.com/b... 這是任何人都可以用來了解前端開發(fā)實(shí)踐的指南。它大致概述并...
摘要:它大致概述并討論了前端工程的實(shí)踐如何學(xué)習(xí)它,以及在年實(shí)踐時(shí)使用什么工具。目的是每年發(fā)布一次內(nèi)容更新。前端實(shí)踐第一部分廣泛描述了前端工程的實(shí)踐。對大多數(shù)人來說,函數(shù)式編程看起來更加自然。 1 Front-End Developer Handbook 2017 地址:https://frontendmasters.com/b... 這是任何人都可以用來了解前端開發(fā)實(shí)踐的指南。它大致概述并...
摘要:函數(shù)式編程,一看這個(gè)詞,簡直就是學(xué)院派的典范。所以這期周刊,我們就重點(diǎn)引入的函數(shù)式編程,淺入淺出,一窺函數(shù)式編程的思想,可能讓你對編程語言的理解更加融會貫通一些。但從根本上來說,函數(shù)式編程就是關(guān)于如使用通用的可復(fù)用函數(shù)進(jìn)行組合編程。 showImg(https://segmentfault.com/img/bVGQuc); 函數(shù)式編程(Functional Programming),一...
閱讀 1411·2021-10-08 10:04
閱讀 733·2021-09-07 09:58
閱讀 2912·2019-08-30 15:55
閱讀 2424·2019-08-29 17:21
閱讀 2126·2019-08-28 18:04
閱讀 3075·2019-08-28 17:57
閱讀 715·2019-08-26 11:46
閱讀 2228·2019-08-23 17:20