如果xs
是一个集合而pred
是一个返回Bool
的闭包,是否有内置函数执行以下操作?
xs.filter(pred).first
这将获得与预测匹配的集合的第一个元素,如果没有匹配则为nil
。对索引不感兴趣,但对元素本身不感兴趣。
答案 0 :(得分:2)
不,没有,但你可以自己写一个:
extension SequenceType {
func first(@noescape pred: Generator.Element throws -> Bool) rethrows -> Generator.Element? {
return try filter(pred).first
}
}
编辑:此版本不是最佳版本,因为filter
创建了一个全新的数组,即使只需要第一个元素。如Martin R所述,lazy.filter
也不起作用。这对于使其与lazy
:
extension CollectionType {
func first(pred: Generator.Element -> Bool) -> Generator.Element? {
return lazy.filter(pred).first
}
}
由于:
@noescape
不能被使用,因为@noescape
意味着闭包不能转义当前函数,这在将它传递给惰性filter
时是可能的(不评估元素)直到它被要求 - >必须逃脱谓词)throws
无法使用,因为过滤器是惰性的 - >错误不会立即抛出,而是在第一次使用时调用first
,但first
不能抛出,因为它是属性。有一些关于在未来的Swift版本中使用getter(和下标)的讨论。CollectionType
,因为只有LazyCollectionType
具有first
属性。所以要真正让它变得懒惰并拥有所有@noescape
,throws
和SequenceType
,你必须使用命令式方法:
extension SequenceType {
func first(@noescape pred: Generator.Element throws -> Bool) rethrows -> Generator.Element? {
for elem in self where try pred(elem) {
return elem
}
return nil
}
}
答案 1 :(得分:0)
在最简单的情况下,您想要的可能如下所示:
let array = [18, 12, 35, 11, 12, 44]
var first: Int?
for element in array where element == 12 {
first = element
break
}
print(first) // prints: Optional(12)
如果确实需要设置谓词闭包,可以使用以下模式:
let array = [18, 12, 35, 11, 12, 44]
var first: Int?
let predicateClosure = { (value: Int) -> Bool in
return value == 12
}
for element in array where predicateClosure(element) {
first = element
break
}
print(first) // prints: Optional(12)
如果您需要重复这些操作,可以使用SequenceType
协议扩展来重构代码:
extension SequenceType where Generator.Element == Int {
func getFirstWithPredicate(predicate: Int -> Bool) -> Int? {
for element in self where predicate(element) {
return element
}
return nil
}
}
let array = [18, 12, 35, 11, 12, 44]
let predicateClosure: Int -> Bool = {
return $0 == 12
}
let first = array.getFirstWithPredicate(predicateClosure)
print(first) // prints: Optional(12)
请注意,您不需要predicate
闭包参数来转义此getFirstWithPredicate(_:)
方法,因此您可以在其前面添加@noescape
属性(请参阅Nonescaping Closures更多细节):
extension SequenceType where Generator.Element == Int {
func getFirstWithPredicate(@noescape predicate: Int -> Bool) -> Int? {
for element in self where predicate(element) {
return element
}
return nil
}
}
let array = [18, 12, 35, 11, 12, 44]
let predicateClosure = { $0 == 12 }
let first = array.getFirstWithPredicate(predicateClosure)
print(first) // prints: Optional(12)
如果您希望以前的代码适用于任何类型的序列,则可以删除Int
约束并重新声明getFirstWithPredicate(_:)
方法,如下例所示:
extension SequenceType {
func getFirstWithPredicate(@noescape predicate: Generator.Element -> Bool) -> Generator.Element? {
for element in self where predicate(element) {
return element
}
return nil
}
}
let intArray = [18, 12, 35, 11, 12, 44]
let firstInt = intArray.getFirstWithPredicate { $0 == 12 }
print(firstInt) // prints: Optional(12)
let stringArray = ["Car", "Boat", "Plane", "Boat", "Bike"]
let firstString = stringArray.getFirstWithPredicate { $0 == "Boat" }
print(firstString) // prints: Optional("Boat")
如果您正在使用符合CollectionType
协议的协议对象(例如,Array
),则可以通过相同的getFirstWithPredicate(_:)
声明轻松获取与谓词匹配的最后一个元素。使用reverse()
:
extension SequenceType {
func getFirstWithPredicate(@noescape predicate: Generator.Element -> Bool) -> Generator.Element? {
for element in self where predicate(element) {
return element
}
return nil
}
}
struct Toy {
let name: String
let price: Int
}
let array = [
Toy(name: "Ball", price: 20),
Toy(name: "Car", price: 12),
Toy(name: "Plane", price: 35),
Toy(name: "Boat", price: 12),
]
let lastToyWithMatchingPrice = array.reverse().getFirstWithPredicate { $0.price == 12 }
print(lastToyWithMatchingPrice) // prints: Optional(Toy(name: "Boat", price: 12))