1. 程式人生 > >淺談 Swift 柯里化(Currying)

淺談 Swift 柯里化(Currying)

       在 Swifter中,第一章 就是 講解 柯里化。這本書 真的是非常的不錯,值得一看,同時,正如作者王巍所說,國內的 大量的流水線書籍真的沒必要買。如果 你希望入門,去這裡,如果 你想提高,我非常推薦這本書。當然 你也可以在這裡找到它的內容。

       繼續說柯里化大笑

       先理解柯里化:把接受多個引數的方法變換成接受第一個引數的方法,並且 返回接受餘下的引數並返回結果的新方法。用數學的思維很好理解,一個函式 xy ,當 傳入 y = 2,返回的 就是 2x。

       寫一個簡單的例子。

    func JustForExample(a:Int)(b:Int) -> Int{ 
        return a + b
    }
        這個方法需要傳入兩個引數,但是我們沒有寫成 (a:Int,b:Int),而是分成了多個括號
       
    func  exampleUse() {
        let exampleAdd = JustForExample(3) //只傳入第一個引數,返回一個函式,函式為 Int ->Int
        let result = exampleAdd(b:7)//傳入 Int 返回 Int
        print(result)
    }
    
       這就是柯里化的簡單應用,當然 柯里化 在 js 等 中 也是很常用的。
       上面這段程式碼簡單,但是下面這段程式碼 就不是很容易理解了。借鑑自:
Ole Begemann

    class BankAccount {
        var balance: Double = 0.0

        func deposit(amount: Double) {
            balance += amount
        }
    }
       定義了一個class,其中有一個方法 deposit(Double),

       正常使用:

    let account = BankAccount()
    account.deposit(100) // balance = now
       利用柯里化我們可以這麼寫:
    let depositor = BankAccount.deposit
    depositor(account)(100) // balance = 200
     

      仔細觀察,我們發現depositor 沒有例項化一個BankAccout物件,而是直接引用他的deposit方法,這就類似於C語言的函式指標。然後通過 給depositor傳入一個例項化物件,返回一個 完整的deposit(Double)函式,然後 傳入一個Double,返回空。

      拆開depositor我們 可以這麼寫:

   let depositor: BankAccount -> (Double) -> ()
      分成兩部分來看,第一部分:
   BankAccount -> (Double) 傳入一個 BankAccount例項,返回一個需要傳入Double的Function,
      第二部分:    
   (Double) -> () 傳入一個Double,返回 空,你應該理解為對 deposit() 的簽名。
       然後是 原文照著這個思路 ,藉助柯里化 ,對target-acton的安全的改造。(因為 Swift的Selector 只能以字串生成,面臨難以重構的問題,並且無法再編譯期間檢查)。

       以下是程式碼:

/// 目標事件協議

protocol TargetAction {
    
    func performAction()
    
}


/**
 
 OC中的委託
 
 事件包裝結構,這裡是泛型,這裡表示傳入的資料型別可以是AnyObject
 
 這個方法遵循TargetAction協議來處理事件
 
 */

struct TargetActionWrapper<T: AnyObject>:TargetAction{
    weak var target: T?
    //柯里化
    let action: (T) -> () -> ()

    
    func performAction() -> () {
        
        if let t = target {
            
            action(t)()
            
        }
        
    }
    
}


/// 列舉事件

enum ControlEvent {
    
    case TouchUpInside
    
    case ValueChanged
    
    //...
    
}


/// 例子

class currying{
    var actions = [ControlEvent :TargetAction]()
    
    func setTarget<T:AnyObject>(target: T,action: (T) -> () -> (),controlEvent:ControlEvent){
        
        actions[controlEvent] = TargetActionWrapper(target:target,action:action)
        print(T)
        print(action)
    }
    
    
    
    
    
    /// 移除
    
    func removeTargetForControlEvent(controlEvent:ControlEvent){
        
        actions[controlEvent] = nil
        
    }
    
    /// 執行
    
    func performActionForControlEvent(controlEvent:ControlEvent){
        
        actions[controlEvent]?.performAction()
        
    }
    
}
         使用:
class ViewController: UIViewController {

    let button = currying()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        button.setTarget(self, action: ViewController.onButtonTap, controlEvent: .TouchUpInside)
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    func onButtonTap(){
        
       
        
    }
    
    func  exampleUse() {
        let exampleAdd = JustForExample(3) //只傳入第一個引數,返回一個函式,函式為 Int ->Int
        let result = exampleAdd(b:7)//傳入 Int 返回 Int
        print(result)
    }
    
    
    func JustForExample(a:Int)(b:Int) -> Int{
        return a + b
    }
    
    
}
          寫在最後:

         不知道為什麼,在你寫上面的example的時候,會告訴你,Swift 會 在不久移除 柯里化 這種 申明語法,推薦使用簡單的多引數設計。當然, 即使 要去掉,這種 有趣的知識, 也是值得學習的微笑