我想添加一个可选的"过滤器";处理字符串列表的函数的参数。如果过滤器是nil
,我想处理所有的字符串,否则我只想处理包含过滤器的字符串。大致:
func process(items: [String], filter: String?) {
for item in items {
if filter == nil || item.contains(filter) {
// Do something with item
}
}
}
类型检查器抱怨将筛选器传递到contains
,因为它是可选的,但contains
需要String
。我当然可以强行打开包装,但这看起来很难看。以上内容将在Kotlin中编译,因为它会明智地丢弃可选内容,但在Swift中表达这一点的惯用方式是什么?
谢谢!
Optional上有一个map
,它仅在可选不是nil
时执行所提供的闭包。如果map
返回nil
,则可以将map
与nil合并运算符??
一起使用以提供true
的默认值,因为filter
是nil
:
func process(items: [String], filter: String?) {
for item in items {
if filter.map(item.contains) ?? true {
// process item
}
}
}
这里的关键是(并且一直是(nil合并运算符。如果Optional不是nil
,则可以安全地打开它,但如果是nil
,则可以替换另一个值。
然而,这个问题的问题在于,它摆出了一种不敏捷的姿态。您永远不会编写一个采用可选字符串的过滤函数。您将编写一个过滤函数,该函数采用Optional过滤函数您希望允许任何类型的数组和任意过滤函数,而不是将调用方限制为contains
和String。这是编写此函数的快捷方式。
当我们处理它时,函数也不应该预先确定对数组成员执行的操作。这应该是调用方传入的另一个函数!
因此,我们最终得出了一个更一般、更Swiftier的目标声明:
func process<T>(_ arr: [T],
filtering filterPred: ((T)->Bool)? = nil,
processing processPred: ((T)->Void)) {
// ...
}
好吧!让我们来写这个函数。processing
部分很简单;这是CCD_ 18调用的谓词。像这样:
func process<T>(_ arr: [T],
filtering filterPred: ((T)->Bool)? = nil,
processing processPred: ((T)->Void)) {
arr.forEach(processPred)
}
非常好!但我们忽略了对CCD_ 19函数的处理。让我们来处理这个问题。显然,这是橡胶与道路交汇的地方。同样显而易见的是,这是一个要传递到filter
中的函数。但只有当它不是nil
时,我们才能通过展开来做到这一点。如果是nil
怎么办?OP已经指定,在这种情况下,我们应该让所有元素通过过滤器。
因此,考虑一个要传递到filter
中的谓词函数。是什么让一切都通过的函数?它是:
{ _ in true }
所以,就是要放在零合并运算符后面的!因此,这就是完整的答案:
func process<T>(_ arr: [T],
filtering filterPred: ((T)->Bool)? = nil,
processing processPred: ((T)->Void)) {
arr.filter(filterPred ?? { _ in true }).forEach(processPred)
}
这里有两个测试:
let arr = [1,2,3,4]
self.process(arr) { print($0) }
self.process(arr) { $0 % 2 == 0 } processing: { print($0) }