javascript的函數組合與柯里化的詳解(附範例)
這篇文章帶給大家的內容是關於javascript的函數組合與柯里化的詳解(附範例),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。
我們都知道單一職責原則,其實物件導向的SOLID中的S(SRP, Single responsibility principle)。在函數式當中每一個函數就是一個單元,同樣應該只做一件事。但是現實世界總是複雜的,當把現實世界映射到程式設計時,單一的函數就沒有太大的意義。這時候就需要函數組合和柯里化了。
鍊式呼叫
如果用過jQuery的都曉得啥是鍊式調用,例如$('.post').eq(1).attr('data- test', 'test')
.javascript原生的一些字串和陣列的方法也能寫出鍊式呼叫的風格:
'Hello, world!'.split('').reverse().join('') // "!dlrow ,olleH"
首先鍊式呼叫是基於物件的,上面的一個一個方法split
, reverse
, join
如果脫離的前面的物件"Hello, world!"是玩不起來的。
而在函數式程式設計中方法是獨立於資料的,我們可以把上面以函數式的方式在寫一遍:
const split = (tag, xs) => xs.split(tag) const reverse = xs => xs.reverse() const join = (tag, xs) => xs.join(tag) join('',reverse(split('','Hello, world!'))) // "!dlrow ,olleH"
你一定會說,你是在逗我。這比鍊式呼叫好在哪裡了?這裡還是依賴資料的啊,沒有傳遞`'Hello, world!',你這一串一串的函數組合也轉不起來啊。這裡唯一的好處也就是那幾個單獨的方法可以重複使用了。莫慌,後面還有那麼多內容我怎麼也會給你優化(忽悠)好的。再進行改造前,我們先介紹兩個概念,部分應用和柯里化。
部分應用
部分應用程式是一種處理函數參數的流程,他會接收部分參數,然後傳回一個函數接收較少的參數。這個就是部分應用。我們用bind
來實作一把:
const addThreeArg = (x, y, z) => x + y + z; const addTwoArg = addThreeNumber.bind(null, 1) const addOneArg = addThreeNumber.bind(null, 1, 2) addTwoArg(2, 3) // 6 addOneArg(7) // 10
上面利用bind
產生了另外兩個函數,分別接受剩下的參數,這就是部分應用。當然你也可以透過其他方式實現。
部分應用存在的問題
部分應用主要的問題在於,它傳回的函數類型無法直接推斷。如前面所說,部分應用程式回傳一個函數接收較少的參數,而沒有規定傳回的參數具體是多少個。這也就是一些隱式的東西,你需要去查看程式碼。才知道傳回的函數接收多少個參數。
柯里化
柯里化定義:你可以調一個函數,但不一次將所有參數傳給它。這個函數會回傳一個函數去接收下一個參數。
const add = x => y => x + y const plusOne = add(1) plusOne(10) // 11
柯里化的函數傳回一個只接收一個參數的函數,而傳回的函數型別可以預測。
當然在實際開發中,有很多的函數都不是柯里化的,我們可以使用一些工具函數來轉換:
const curry = (fn) => { // fn可以是任何参数的函数 const arity = fn.length; return function $curry(...args) { if (args.length <p>也可以用開源函式庫Ramda裡提供的curry方法。 </p><h3 id="哦-柯里化-有什麼用呢">哦,柯里化。有什麼用呢? </h3><p>舉例</p><pre class="brush:php;toolbar:false">const currySplit = curry((tag, xs) => xs.split(tag)) const split = (tag, xs) => xs.split(tag) // 我现在需要一个函数去split "," const splitComma = currySplit(',') //by curry const splitComma = string => split(',', string)
可以看到柯里化的函數產生新函數時,和資料完全沒有關係。對比兩個產生新函數的過程,沒有柯里化的相對而言就有一點囉嗦了。
函數組合
先給程式碼:
const compose = (...fns) => (...args) => fns.reduceRight((res, fn) => [fn.call(null, ...res)], args)[0];
其實compose做的事情一共兩件:
接收一組函數,傳回一個函數,不立即執行函數
組合函數,將傳遞給他的函數從左到右組合。
可能有同學對上面的reduceRight不是很熟悉,我給個2元和3元的例子:
const compose = (f, g) => (...args) => f(g(...args)) const compose3 = (f, g, z) => (...args) => f(g(z(...args)))
函數呼叫是從左到右,數據流也是一樣的從左到右。當然你可以定義從右到左的,不過從語意上來說就沒那麼表意了。
好,現在讓我們來優化一下最開始的例子:
const split = curry((tag, xs) => xs.split(tag)) const reverse = xs => xs.reverse() const join = curry((tag, xs) => xs.join(tag)) const reverseWords = compose(join(''), reverse, split('')) reverseWords('Hello,world!');
是不是簡潔容易理解多了。這裡的reverseWords
也是我們之前講過的Pointfree的程式碼樣式。不依賴資料和外部狀態,就是組合在一起的函數。
Pointfree我在上一篇介紹過JS函數式程式設計 - 概念,也闡述了其優缺點,有興趣的小夥伴可以看看。
函數組合的結合律
先回顧一下小學知識加法結合律:a (b c)=(a b) c
。我就不解釋了,你們應該能理解。
這對我們程式設計其實也存在結合律的:
compose(f, compose(g, h)) === compose(compose(f, g), h);
這個對我們程式設計有一個好處,我們的函數組合可以隨意組合並且快取:
const split = curry((tag, xs) => xs.split(tag)) const reverse = xs => xs.reverse() const join = curry((tag, xs) => xs.join(tag)) const getReverseArray = compose(reverse, split('')) const reverseWords = compose(join(''), getReverseArray) reverseWords('Hello,world!');
腦圖補充:
以上是javascript的函數組合與柯里化的詳解(附範例)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

JavaScript教學:如何取得HTTP狀態碼,需要具體程式碼範例前言:在Web開發中,經常會涉及到與伺服器進行資料互動的場景。在與伺服器進行通訊時,我們經常需要取得傳回的HTTP狀態碼來判斷操作是否成功,並根據不同的狀態碼來進行對應的處理。本篇文章將教你如何使用JavaScript來取得HTTP狀態碼,並提供一些實用的程式碼範例。使用XMLHttpRequest

C++lambda表達式為函數式程式設計帶來了優勢,包括:簡潔性:匿名內嵌函數,提升程式碼可讀性。程式碼重用:可傳遞或儲存lambda表達式,方便重複使用程式碼。封裝:提供封裝程式碼段的方法,無需建立單獨函數。實戰案例:過濾列表中的奇數。計算列表中元素的總和。 lambda表達式實現了函數式程式設計的簡潔性、可重複使用性和封裝性。

透過使用惰性資料結構,可以在Go語言中實現惰惰求值:建立一個包裝器類型,封裝實際值,僅在需要時才計算。在函數式程式中最佳化斐波那契數列的計算,延後中間值的計算,直到實際需要。這可以消除不必要的開銷,提高函數式程式的效能。

在Go中使用函數式程式設計時需要注意五個常見錯誤和陷阱:避免引用意外修改,確保傳回新建立的變數。解決並發性問題,使用同步機製或避免捕獲外部可變狀態。謹慎使用偏函數化,以提高程式碼可讀性和可維護性。始終處理函數中的錯誤,確保應用程式的健全性。考慮效能影響,使用內聯函數、扁平化資料結構和操作批次來最佳化程式碼。

JS-Torch簡介JS-Torch是一種深度學習JavaScript函式庫,其語法與PyTorch非常相似。它包含一個功能齊全的張量物件(可與追蹤梯度),深度學習層和函數,以及一個自動微分引擎。 JS-Torch適用於在JavaScript中進行深度學習研究,並提供了許多方便的工具和函數來加速深度學習開發。圖片PyTorch是一個開源的深度學習框架,由Meta的研究團隊開發和維護。它提供了豐富的工具和函式庫,用於建立和訓練神經網路模型。 PyTorch的設計理念是簡單和靈活,易於使用,它的動態計算圖特性使

python中的Lambda表達式是匿名函數的另一種語法形式。它是一個小型匿名函數,可以在程式中任何地方定義。 Lambda表達式由一個參數列表和一個表達式組成,表達式可以是任何有效的Python表達式。 Lambda表達式的語法如下:lambdaargument_list:expression例如,下面的Lambda表達式傳回兩個數字的和:lambdax,y:x+y這個Lambda表達式可以傳遞給其他函數,例如map()函數:numbers=[ 1,2,3,4,5]result=map(lambda

pythonLambda表達式是一個強大且靈活的工具,可用於建立簡潔、可讀且易於使用的程式碼。它們非常適合快速建立匿名函數,這些函數可以作為參數傳遞給其他函數或儲存在變數中。 Lambda表達式的基本語法如下:lambdaarguments:expression例如,以下Lambda表達式將兩個數字相加:lambdax,y:x+y這個Lambda表達式可以傳遞給另一個函數作為參數,如下所示:defsum( x,y):returnx+yresult=sum(lambdax,y:x+y,1,2)在這個例子

Go和Node.js在類型化(強/弱)、並發(goroutine/事件循環)、垃圾收集(自動/手動)上有差異。 Go具備高吞吐量、低延遲,適用於高負載後端;Node.js擅長異步I/O,適合高並發、短請求。兩者的實戰案例包括Kubernetes(Go)、資料庫連線(Node.js)、網路應用程式(Go/Node.js)。最終選擇取決於應用程式需求、團隊技能和個人偏好。
