NSCookies

提供一些OC与Swift开发的小技巧,让大家可以花几分钟学到一些好玩的东西

装个逼:自定义三元运算符

最近刚看完@喵神翻译的函数式Swift这本书(有兴趣的小伙伴可以去买来看看,记得支持正版哟~),然后就想说说 Swift 中的自定义操作符,这个在函数式编程中也是非常有意义的工具。例如在 Applicative Functor 中的 <*>Monad 中的 >>= 在我们自己实现函数式编程中都有可能需要自定义来实现这两个操作符。另外,除了在函数式编程里,我们平常的编程工作中也有经常遇到需要重载或者自定义操作符的时候,例如实现了 Equatable 协议的情况下就需要重写 == 操作符。

但是这里并不是手把手教你如何自定义操作符,而是来说说在 Swift 2 和 3 中自定义操作符有什么不同吧,至于英文好的小伙伴可以参考SE-0077

在 Swift 2 的情况下,我们通常通过下面的代码来定义自己的操作符:

// Swift 2
infix operator || {  
associativity: left  
precedence: 140  
}

func ||<T>(left: T, right: T) -> T {  
    // do something
}

而在 Swift 3 中我们则通过下面方式来进行替代:

// Swift 3
precedencegroup OrGroup {  
    associativity: left
    higherThan: AdditionPrecedence
    lowerThan: MultiplicationPrecedence
}

infix operator ||: OrGroup

func ||<T>(left: T, right: T) -> T {  
    // do something
}

整体上来说 Swift 2 和 3 之间是没有特别大的改动,只是将格式方面进行一种更加优雅的进化而已。虽然进行了这样的调整,但是有一点让宝宝不爽的是,已经定义的 group 在 Xcode 中竟然不给自动提示,特别是对于一些标准库中已经定义的 group 竟然也不给提示,这就让宝宝不开心了。目前的解决方案就是通过SE-0077这里来查询。

好了,最后我们来简单说说如何实现三元操作符吧,也当是给个自定义操作符的小结。我们都知道 Swift 跟其他大多数语言一样,对于三元操作符只提供了条件操作符 _ ? _ : _ ,那么如果我们想自己定义三元操作符怎么办呢?记得有一句话是这么说的,方法总比问题多~

其实我们可以创造一个伪三元操作符,就是用两个二元操作符来结合产生一个三元操作符,例如我们用 _ ??? _ ||| _ 来实现一下条件操作符的功能。首先我们先定义一下两个操作符:

// Swift 3
precedencegroup QuesGroup {  
    associativity: left
}

infix operator ???: QuesGroup

precedencegroup OrGroup {  
    associativity: left
    higherThan: QuesGroup
}

infix operator |||: OrGroup  

为什么这里的 OrGroup 要用 higherThan: QuesGroup 呢?我们设想一下,如果是 QuesGroup 中用 higherThan: OrGroup 的话,那么我们自定义的条件表达式就变成了 (_ ??? _) ||| _ 这种形式了,而是不是 _ ??? (_ ||| _) 这种形式。

其次我们来考虑一下条件操作符的流程,它是先查看?前的布尔值,然后再根据布尔值来决定返回哪一个部分,那么我们可以认为后面一部人是需要一个布尔值作为参数,返回数据的方法。并且根据其有短路的功能,我们可以采用 @autoclosure 来实现延迟加载以及短路的功能。并且根据上面的说法我们知道第二个参数和第三个参数其实是一个整体,并且根据前面来返回一个值。具体实现就如下面:

// Swift 3
func |||<T>(left: @autoclosure @escaping () -> T, right: @autoclosure @escaping () -> T) -> (Bool) -> T {  
    return { condition in
        if condition {
            return left()
        } else {
            return right()
        }
    }
}

func ???<T>(condition: @autoclosure @escaping () -> Bool, value: (Bool) -> T) -> T {  
    return value(condition())
}

代码非常简单,测试代码就更简单了:

let bTrue = true  
let bFalse = false

bTrue ??? "true value" ||| "false value"  
// 输出 true value
bFalse ??? "true value" ||| "false value"  
// 输出 false value

到这里就搞定了自定义"三元操作符"了~炒鸡简单。那么既然回了三元,那要定义四元,五元甚至更多元都可以采用相同的方式。只要分析好逻辑,并写出来就好了,好吧~就这样了。

宝宝去被窝里蓝瘦,香菇了~在录一个视频吓死你们

PS:具体代码可以从Github上获取。

如有问题或纠正, 可以联系@叫什么都不如叫Pluto-Y或在Github

Pluto Y
作者

Pluto Y

嗨,我叫章龙华,一名由Java EE转到iOS的程序猿。不断的修行与前进,喜欢学习新的知识,寻找志同道合之人。不断努力着,为了明天的更轻松。