鉴于此代码:
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
答案 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)
}
}
}