摘要:在我們上面的例子里,的輸入和返回值是一樣的數據類型,都是。所以我們可以寫一個服務于函數的么答案是肯定的但怎么拆開一個函數是個問題簡單點兒,你可以直接執行這個函數,然后用她的返回值。我們來拿試一下聲明了一個函數的參數及返回值的形態。
Functors
先看看如下代碼:
function plus1(value) { return value + 1; }
這就是一個普通函數,接收一個integer作為參數,再加1返回。類似的,我們還能再來一個加2的函數。稍后我們會用到這幾個函數:
function plus2(value) { return value + 2; }
下面我們來寫一個如下的組合函數,來按需執行上述函數
function F(value, fn) { return fn(value); } F(1, plus1) ==>> 2
當傳入正確的integer參數時,這個組合函數F工作正常,那如果傳入的數據類型是Array呢?
F([1, 2, 3], plus1) ==>> "1,2,31"
我擦,我們給了一個Array of integers,想讓她們和plus1中的數值相加,結果返回的是個string?!結果不對不說,我們是以Array開頭兒的,結果返回一個string,類型也沒對上啊!換句話說,我們這個程序把輸入的結構也給整岔劈了。我們還是希望函數F能做“正確的事兒”-維護輸入參數的數據結構,并使其被各handler正確處理。
OK,我這兒說的“維護輸入參數的數據結構”是?這個函數F應該把傳入的Array拆開并得到其中的每一個值。然后依次交給handler處理。然后把handler處理后的各個結果再封裝成一個新的Array,然后返回這個新Array。好消息是我這一堆廢話說的東西都不用你寫了,JavaScript已經寫好這么一個函數了,她叫map
[1, 2, 3].map(plus1) ==>> [2, 3, 4]
map就是一個functor!
functor就是一個函數,接收一個數值、一個handler,然后做事兒!
再說細點兒
functor就是一個函數,接收一個數值、一個handler,拆開傳入的原始值,得到其中的各個分解后的值,然后調用handler依次處理每一個上一步的到的數據,再將處理后的多個數據再封裝成一個新的結構體,最后返回這個新的結構體。
這里要注意傳入值的類型,拆開后的各個數值可能是原始數據類型,也可能是集合。
而且,最后返回的數據類型也不必一定和傳入的數據類型一模一樣。在我們上面的例子里,map的輸入和返回值是一樣的數據類型,都是Array。返回的數據結構可以是任意結構,只要能分別獲取其中的數值即可。所以,假設你有一個函數,接收一個Array作為參數,但返回一個包含了keys的Object,每個key都指向一個對應的數值,那這也是一個functor。
在JavaScript里,filter就是一個functor,因為她依舊返回一個Array,但forEach就不是,因為她返回undefined。這就是我們說的,forEach沒有“維護輸入參數的數據結構”。
Functors是數學里關于"homomorphisms between categories"的概念,不懂沒關系,我們分別換個詞兒再讀一下:
homo = 一些、多個
morphisms = 維護數據結構的函數
category = 類型
根據上述詞匯分析,函數F可以看作是兩個普通函數f和g的組合。
F(f . g) = F(f) . F(g)
其中.就是表示組合。即:functors必須保存組合特性。
基于這個方程式,我們就能得出一個函數是否是functor的結論。
Array Functor剛才我們看到map就是一個操作Array的functor。下面我們來證明一下Array.map就是一個functor。
function compose(f, g) { return function(x) { return f(g(x)); }; }
組合多個函數就是通過將前一個函數的執行結果傳給后一個函數作為參數的形式來依次調用多個函數。注意:我們上面這個compose是從右向左執行的,將g的執行結果傳給f。
[1, 2, 3].map(compose(plus1, plus2)) ==>> [ 4, 5, 6 ] [1, 2, 3].map(plus2).map(plus1) ==>> [ 4, 5, 6 ]
看看!隨你怎么寫,結果都一樣。 所以map就是一個functor。
下面我們來試試其他functors。functors傳入參數的類型可以是任意類型,只要你有辦法拆開她的值,然后返回一個新的數據結構。
String FunctorOK,那我們是不是也可以寫一個能處理string的functor?
先來文個問題,你能“拆開”一個string么? 必須的呀(這里如果有疑問,就是你的不對了哦),如果你把一個string當成一個Array of chars,是不是可以拆開了?所以啊,問題就在于你是如何思考的。
然后,我們也知道每一個char其實都有一個integer的char code。那我都可以使用上面的plus1操作每一個char,然后將所有結果封裝回string,再返回!
function stringFunctor(value, fn) { var chars = value.split(""); return chars.map(function(char) { return String.fromCharCode(fn(char.charCodeAt(0))); }).join(""); } stringFunctor("ABCD", plus1) ==>> "BCDE"
開始感受到牛逼之處了么?估計你都能基于string functor寫一個XX解析器了。
Function Functor在JavaScript中,函數是一等公民(first class citizens)。意思是你可以像其他任何類型那樣使用函數。所以我們可以寫一個服務于函數的functor么?
答案是肯定的!
但怎么拆開一個函數是個問題!簡單點兒,你可以直接執行這個函數,然后用她的返回值。不過傻子也知道這肯定有問題(執行是需要參數的)!切記這里我們一定要把傳入的函數本身當做那個傳入的“值”。明確了這一點,我們只要再返回一個函數,看上去就是functor了吧?當這個返回的函數執行時,我們傳入指定參數,其實她內部是將傳入的參數遞給value函數,再將value(initial)的結果傳給fn,最后的到最終返回值。
function functionFunctor(value, fn) { return function(initial) { return function() { return fn(value(initial)); }; }; } var init = functionFunctor(function(x) {return x * x}, plus1); var final = init(2); final() ==> 5
說白了,上面這個Function functor沒做什么特別的事。但要注意的是,除非你最終執行該functor,否則什么事都不會發生哦!所有東西都被暫存起來,直到你執行最終functor。由Function functor可以衍生出來其他函數式編程的內容,譬如:狀態維護、連續調用甚至是Promise。有興趣的,可以自己嘗試根據已學知識來把這幾個概念實現一下。
MayBe Functorfunction mayBe(value, fn) { return value === null || value === undefined ? value : fn(value); }
看,這也是個合法的functor。
mayBe(undefined, compose(plus1, plus2)) ==>> undefined mayBe(mayBe(undefined, plus2), plus1) ==>> undefined mayBe(1, compose(plus1, plus2)) ==>> 4 mayBe(mayBe(1, plus2), plus1) ==>> 4
mayBe通過了我們上面的測試。這兒還真沒有什么拆開和重新封裝。如果傳入是空,那返回也是空。mayBe是一中簡單有效的路徑選擇函數,和一下這種寫法相同:
if (result === null) { return null; } else { doSomething(result); }Identity Function
function id(x) { return x; }
上面這個就是所謂的identity function。她就把傳入的參數又返回了一遍。她就是這么叫的,我也沒轍,因為在數學計算里,這就表示組合函數的ID。
前面我們學了functor就是要保存組合特性。其實functor也得保存她的identity。
F(value, id) = value
我們來拿map試一下
[1, 2, 3].map(id) ==>> [ 1, 2, 3 ]Type Signature
Type Signature聲明了一個函數的參數及返回值的形態。那之前我們寫的plus1函數的Type Signature就是:
f: int -> int
map作為functor,她的Type Signature依賴于handler的Type Signature。譬如:map和plus1組合使用的話,她的Type Signature是:
map: [int] -> [int]
不過,由于handler的Type Signature不必前后一致,如下:
f: int -> string
那map的Type Signature也可以是:
map: [int] -> [string]
這就是說,類型變化不會影響functor的函數組合特性。一般來說,functor的Type Signature可以這么定義:
F: A -> B
舉例說,就是map可以傳入數值數組,但是卻返回一個字符串數組,她依舊是functor。
Monads是一種特殊類型的functor,定義如下:
M: A -> A
更多內容,且看下回分解!
原文地址:Functional JavaScript - functors
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/78894.html
摘要:僅在幾年以前,僅有少數的程序員知道函數式編程是什么。函數式編程是聲明性的而不是命令式的應用狀態流經純函數中。函數式編程是一種編程模式。在理解軟件是如何使用函數式編程構建時,理解函數組合是非常重要的一步。不可變性是函數式編程的核心概念。 函數式編程已然變成了一個javascript語言中一個非常熱門的話題。僅在幾年以前,僅有少數的js程序員知道函數式編程是什么。但是在過去三年中,我所見過...
摘要:避免脆弱的基類問題。紅牌警告沒有提到上述任何問題。單向數據流意味著模型是單一的事實來源。單向數據流是確定性的,而雙向綁定可能導致更難以遵循和理解的副作用。原文地址 1. 你能說出兩種對 JavaScript 應用開發者而言的編程范式嗎? 希望聽到: 2. 什么是函數編程? 希望聽到: 3. 類繼承和原型繼承的不同? 希望聽到 4. 函數式編程和面向對象編程的優缺點? ...
摘要:前端日報精選漫談函數式編程一十年蹤跡的博客前端每周清單的優勢與劣勢有望超越在嵌入式及物聯網的應用現狀進階系列高階組件詳解一前端之路譯如何充分利用控制臺掘金程序猿升級攻略眾成翻譯中文譯如何充分利用控制臺掘金前端從強制開啟壓縮探 2017-06-27 前端日報 精選 漫談 JS 函數式編程(一) - 十年蹤跡的博客前端每周清單: Vue的優勢與劣勢;Node.js有望超越Java;JS在嵌...
摘要:正在失業中的課多周刊第期我們的微信公眾號,更多精彩內容皆在微信公眾號,歡迎關注。若有幫助,請把課多周刊推薦給你的朋友,你的支持是我們最大的動力。是一種禍害譯本文淺談了在中關于的不好之處。淺談超時一運維的排查方式。 正在失業中的《課多周刊》(第3期) 我們的微信公眾號:fed-talk,更多精彩內容皆在微信公眾號,歡迎關注。 若有幫助,請把 課多周刊 推薦給你的朋友,你的支持是我們最大的...
摘要:正在失業中的課多周刊第期我們的微信公眾號,更多精彩內容皆在微信公眾號,歡迎關注。若有幫助,請把課多周刊推薦給你的朋友,你的支持是我們最大的動力。是一種禍害譯本文淺談了在中關于的不好之處。淺談超時一運維的排查方式。 正在失業中的《課多周刊》(第3期) 我們的微信公眾號:fed-talk,更多精彩內容皆在微信公眾號,歡迎關注。 若有幫助,請把 課多周刊 推薦給你的朋友,你的支持是我們最大的...
閱讀 3424·2021-11-15 11:39
閱讀 1563·2021-09-22 10:02
閱讀 1313·2021-08-27 16:24
閱讀 3600·2019-08-30 15:52
閱讀 3415·2019-08-29 16:20
閱讀 828·2019-08-28 18:12
閱讀 553·2019-08-26 18:27
閱讀 721·2019-08-26 13:32