Swift3.0 Currying

weixin_33918357發表於2016-11-30

Currying是把接受多個引數的函式變換成接受一個單一引數(最初函式的第一個引數)的函式,並且返回接受餘下的引數而且返回結果的新函式的技術.

在某些情況下,你可能會用某個相同的引數重複呼叫某個方法,那麼利用柯里化會使程式碼更易於維護,Ex:

func sum(a: Int)(b: Int) -> Int {
    return a + b
}
var sumWithFive = sum(5)
sumWithFive(b: 5)
sumWithFive(b: 10)
sumWithFive(b: 15)

但是,Curring特性已經在Swift3.0中被remove了,The reason was:</br>
Curried function declaration syntax func foo(x: Int)(y: Int) is of limited usefulness and creates a lot of language and implementation complexity. We should remove it.</br>
(函式的 currying 特性的使用場景並不大,但他會增加很多語言的複雜性,所以需要刪除它)

�Well,你高興就好,如果我們可能有需求要用到這個,怎麼辦?

其實也是有類似的寫法的:

 func caculate(_ num1: Int)->(Int)->Int{ return { num2 in num1 + num2} }
 let caculateOne = caculate(5)
 caculateOne(2)//7
 caculateOne(3)//8

所以,昨天改造了一下之前寫過的程式碼,寫了一點閉包柯里化的東西(我知道函式的你們肯定都會寫!):

複製替換掉ViewController就可以直接用,但是肯定報錯因為要換一張圖片:]

class ViewController: UIViewController
{
    private lazy var postCardView:UIImageView =
    {
        let postCardView = UIImageView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
        postCardView.center = self.view.center
        postCardView.image = UIImage(named: "1.jpg")
        postCardView.layer.cornerRadius = 5.0
        postCardView.clipsToBounds = true
        
        return postCardView
    }()
    
    override func viewDidLoad()
    {
        super.viewDidLoad()
      
        setUp()
    }
    
    func setUp()
    {
        view.layer.shadowColor = UIColor.black.cgColor
        view.layer.shadowOffset = CGSize(width: 0, height: 10)
        view.layer.shadowRadius = 10.0
        view.layer.shadowOpacity = 0.3
        
        view.addSubview(postCardView)
        view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(panInCard(sender:))))
    }
    func panInCard(sender:UIPanGestureRecognizer)
    {
        let point = sender.location(in: view)
        
        let factor:(CGFloat)->(CGFloat)->CGFloat =
        {
            point in
            {
                length in
                min(1, max(-1, (point - length * 0.5) / length * 0.5))
            }
        }
//        let factor:(CGFloat,CGFloat)->CGFloat = {  point,length in min(1, max(-1, (point - length * 0.5) / length * 0.5))  }

        let transformWithM34:(CGFloat)->(CGFloat)->(CGFloat)->CATransform3D =
        {
            m34 in
            {
                xf in
                {
                    yf in
                    var t = CATransform3DIdentity
                    t.m34 = m34
                    t = CATransform3DRotate(t, CGFloat(M_PI/9) * xf, 0, 1, 0)
                    t = CATransform3DRotate(t, CGFloat(M_PI/9) * yf, -1, 0, 0)
                    return t
                }
            }
        }

        switch sender.state
        {
        case .changed:
            let (xFactor,yFactor) = (factor(point.x)(view.bounds.size.width),factor(point.y)(view.bounds.size.height))
            postCardView.layer.transform = transformWithM34(1.0 / -400)(xFactor)(yFactor)
        case .ended:
            UIView.animate(withDuration: 0.2, animations:{
                self.postCardView.layer.transform = CATransform3DIdentity
            })
        default:
            break
        }
    }
}

通過對factor這個閉包的柯里化和正常寫法可以發現,柯里化會很快增加程式碼的複雜程度,但同時又稍微提升了一點閱讀性。

然而對於下面的三引數閉包transformWithM34,程式碼就已經變得複雜多了。很多的括號看起來快比上if巢狀了。但是不知道親愛的讀者你們有沒有發現,
let transformWithM34:(CGFloat)->(CGFloat)->(CGFloat)->CATransform3D並沒有指定形參名,也就是依次傳入3個CGFloat型別的引數就可以收穫一個CATransform3D的返回值。

那麼

考慮引數的傳入順序,同樣可以達到簡化程式碼的目的,比如我們可以將比較固定的引數先傳入,將變化的引數寫在後面,這樣當需要程式碼複用的時候,會比較方便。 舉個?:

        let datePrint:(Int)->(Int)->(String)->Void =
        {
            month in
            print("\(month)月")
            return{
                day in
                print("\(day)日")
                return{
                    action in print("\(action)")
                }
            }
        }
        let actionPrint = datePrint(2016)(11)
        actionPrint("要出去玩")
        actionPrint("要回家")

動作相對於某一天來說,時間是固定的,動作是變化的。我們如果想輸出那一天的任何動作只需要actionPrint("xxx")就可以了。還是很方便的。
⬇️(本來是想寫個年月日的例子,後來感覺可能沒有這個有說服力,結果引數忘了改了,輸出有點奇怪?)⬇️

2887758-1189eafe28682182.png
Paste_Image.png

最後想說的是。

�最好不要在需要和很多UI控制元件互動的地方用這種多引數柯里化的閉包或者函式。。你懂得。
這種程式碼適用範圍估計也就是那些小巧或者即將變得小巧的工具方法了吧?

感謝您為閱讀這篇文章付出了時間,謝謝。

相關文章