Swift-Currying(柯里化)

1、什么是柯里化?

柯里化是把接受多個(gè)參數(shù)的函數(shù)變換成接受一個(gè)單一參數(shù)(最初函數(shù)的第一個(gè)參數(shù))的函數(shù),并且返回接受余下的參數(shù)切返回結(jié)果的新函數(shù)的技術(shù)

用數(shù)學(xué)理解就是:一個(gè)函數(shù)求xy,當(dāng)傳入y=2時(shí),返回的就是2x

2、簡單示例

例如:實(shí)現(xiàn)一個(gè)函數(shù),輸入的是任一整數(shù),輸出要返回輸入的整數(shù)+2

一般的寫法是

func addTwo(_ a : Int)->Int{
        return a+2
    }
image.gif

上面這種寫法就只是簡單的可以實(shí)現(xiàn)這個(gè)函數(shù),并沒有進(jìn)行優(yōu)化,且不通用,里面的+2直接固定寫死了,如果要實(shí)現(xiàn)+4/+6/+8,不能每次都去重新定義一個(gè)函數(shù),我們需要定義一個(gè)通用的函數(shù),所以需要做如下改進(jìn),主要利用的是swift的currying技術(shù)

func addTwo(_ a : Int)->(Int)->Int{
        return {b in
            return a+b
        }
    }
image.gif

上面的函數(shù)可以簡化成

func addTwo(_ a : Int)->(Int)->Int{
        return {b in a+b }
    }
image.gif

還可以更通用一些,將參數(shù)定義為泛型

    //兩個(gè)參數(shù)的泛型
    func curry<A, B>(_ function:@escaping (A)->B)->(A)->B{
        return {a in function(a)}
    }
    //三個(gè)參數(shù)的泛型
    func curry<A, B, C>(_ function:@escaping (A, B)->C)->(A)->(B)->C{
        return {a in {b in function(a,b)}}
    }
    //四個(gè)參數(shù)的泛型
    func curry<A, B, C, D>(_ function:@escaping (A, B, C)->D)->(A)->(B)->(C)->D{
        return {a in {b in {c in function(a, b, c)}}}
    }

    //currying調(diào)用
    func addTwo(_ a : Int, _ b : Int)->Int{
        return a+b
    }

    let result = curry(addTwo)(1)(2) //打印的結(jié)果為3
image.gif

3、項(xiàng)目中的實(shí)際應(yīng)用

主要應(yīng)用在需要傳多個(gè)參數(shù)的函數(shù),

1)例如:假設(shè)有這樣一個(gè)需求,我需要記錄某個(gè)系統(tǒng)的日志,日志需要包含以下幾個(gè)要素:操作人的名字name,時(shí)間time,日志類型type和日志內(nèi)容msg。

    func curry<A, B, C, D, E>(_ function:@escaping (A, B, C, D)->E)->(A)->(B)->(C)->(D)->E{
        return {a in {b in {c in {d in function(a, b, c, d)}}}}
    }

    func createLogInfo(_ name : String, _ time : String, _ type : String, _ msg : String)->String{
        return "name : \(name)\n" + "type : \(type)\n" + ("message : \(msg)\n" + "time: \(time)    ")
    }

    //調(diào)用
    let createLogInfoResult = curry(createLogInfo)("functionName")("today")("Error")("somethingWrong")

輸出結(jié)果:
name : functionName
type : Error
message : somethingWrong
time: today
image.gif

2)封裝target-action,對其安全的改造

原因:由于swift的selector智能是字符串生成,面臨難以重構(gòu)的問題,并且無法在編譯期間檢查

改造的步驟:

(1)定義一個(gè)目標(biāo)事件協(xié)議

//目標(biāo)事件協(xié)議
protocol TargetAction {
    func performAction()
}
image.gif

(2)定義一個(gè)類,遵循(1)中的協(xié)議來處理事件

/**

 OC中的委托

 事件包裝結(jié)構(gòu),這里是泛型,這里表示傳入的數(shù)據(jù)類型可以是AnyObject

 這個(gè)方法遵循TargetAction協(xié)議來處理事件

 */

struct TargetActionWrapper<T: AnyObject>:TargetAction {

    weak var target : T?

    //柯里化
    let action : (T) -> () -> ()

    func performAction() {
        if let t = target {
            action(t)()
        }
    }
}
image.gif

(3)枚舉事件的類型

//枚舉
enum ControlEvent{
    case TouchUpInside
    case ValueChanged
    //...
}
image.gif

(4)示例

//示例
class currying{
    var actions = [ControlEvent : TargetAction]()
    func setTarget<T: AnyObject>(_ target : T, _ action : @escaping (T)->()->(), _ controlEvent : ControlEvent){
        actions[controlEvent] = TargetActionWrapper(target: target, action: action)
        print("T \(T.self)")
        print("action \(action)")
    }

    //移除
    func removeTargetForControlEvent(_ controlEvent : ControlEvent){
        actions[controlEvent] = nil
    }

    //執(zhí)行
    func performActionForControlEvent(_ controlEvent : ControlEvent){
        actions[controlEvent]?.performAction()
    }
}
image.gif

(5)在項(xiàng)目中的實(shí)際使用

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        currying().setTarget(self, ViewController.btnclick, .TouchUpInside)

    }

     func btnclick(){
        print("點(diǎn)擊了")
    }
}
image.gif
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容