使用"!"包含

时间:2016-12-23 00:29:12

标签: swift boolean

鉴于此代码:

let a = [1, 2, 3, 4, 5]
let b = [5, 6, 7, 8, 9]

let result = a.filter(b.contains)

print(result) // [5]

但如果我想let result = a.filter { !b.contains($0) }怎么办?我怎么能像上面的例子那样做?

let result = a.filter { !b.contains($0) } // works
let result = a.filter(!b.contains)        // fails

2 个答案:

答案 0 :(得分:2)

let result = a.filter { !b.contains($0) }

这是语法糖:

let result = a.filter({ !b.contains($0) })

filter采用函数值。 {...}是一个文字函数值(“闭包”),就像1是一个文字Int值一样。所以没关系。在闭包内,您将!前缀运算符应用于b.contains($0)的结果。那也没关系。

let result = a.filter(!b.contains)        // fails

同样,filter采用函数值。 b.contains是一个函数值,因此该部分很好,这就是a.filter(b.contains)工作的原因。但是!前缀运算符不能应用于函数值,因此失败。

Anurag演示了如何编写!的实现,它确实接受了一个函数并返回一个函数,但这会使IMO感到困惑,因为这不是该运算符的标准用法,而且这些运算符重载往往会降低编译器的速度(有时会非常显着,因为它必须追逐更多类型的可能性)。虽然有多种方法可以尝试“使这项工作”,但这里最好的Swift通常是闭包语法,因为它清晰且标准。

那就是说,如果你做了很多这样的事情,一个合理的方法是创建一个not函数,它接受一个函数并返回一个函数,比如Anurag的!,但没有超载运营商:

func not<T>(_ predicate: @escaping (T) -> Bool) -> (T) -> Bool {
    return { !predicate($0) }
}

然后你可以致电a.filter(not(b.contains))。但同样,如果构建复杂功能是您计划的重要组成部分,我可能只会这样做。

答案 1 :(得分:1)

您始终可以创建自定义运算符。

prefix func !<T>(_ predicate: @escaping (T) -> Bool) -> (T) -> Bool {
    return { element in
        return !predicate(element)
    }
}

然后a.filter(!b.contains)将有效。

或者,如果感觉操作员超载滥用,那么您可以扩展Sequence并将其用作a.filter(b.not(b.contains)),但这有点难看:

extension Sequence {
    func not(_ predicate: @escaping (Self.Iterator.Element) -> Bool) -> (Self.Iterator.Element) -> Bool {
        return { element in
            return !predicate(element)
        }
    }
}