是否可以缩短对更高级别功能的评估?
Is it possible to cut short evaluation of a higher-level function?
我正在寻找一种在评估部分输入序列后停止更高级别函数的方法。
考虑在满足特定条件的序列中查找第一个索引时的情况。例如,假设我们正在寻找 Int
数组 a
中的第一个位置,其中两个连续值的总和大于 100。
你可以用一个循环来做,像这样:
func firstAbove100(a:[Int]) -> Int? {
if a.count < 2 {
return nil
}
for i in 0..<a.count-1 {
if a[i]+a[i+1] > 100 {
return i
}
}
return nil
}
一旦发现感兴趣的位置,循环就会停止。
我们可以使用 reduce
重写这段代码,如下所示:
func firstAbove100(a:[Int]) -> Int? {
if a.count < 2 {
return nil
}
return (0..<a.count-1).reduce(nil) { prev, i in
prev ?? (a[i]+a[i+1] > 100 ? i : nil)
}
}
但是,这种方法的缺点是 reduce
一直到 a.count-2
,即使它在第一个索引处找到了匹配项。结果是一样的,但能减少不必要的工作就好了。
有没有办法让 reduce
停止尝试进一步的匹配,或者可能有一个不同的函数让你在找到第一个匹配后停止?
indexOf
将在找到第一个匹配项后停止,因此您可以将 firstAbove100
重写为如下内容:
func firstAbove100(a:[Int]) -> Int? {
return a.count > 1 ? (a.startIndex..<a.endIndex-1).indexOf({ a[[=10=]] + a[[=10=] + 1] > 100 }) : nil
}
如前所述,reduce
是专门为评估整个序列而设计的,因此不是为了短路而设计的。以这种方式使用它来查找满足给定谓词的元素的索引最好使用 indexOf
as .
另外,从 Swift 3 开始,Sequence
上现在有一个 first(where:)
函数,可以让您找到满足给定谓词的第一个元素。这可能是比 indexOf
更合适的替代方案,因为它 return 是元素而不是索引(尽管在您的特定示例中它们是相同的)。
你可以这样写你的例子:
func firstAbove100(_ a:[Int]) -> Int? {
guard a.count > 1 else {return nil}
return (0..<a.count-1).first { i in
a[i]+a[i+1] > 100
}
}
然而,如果你想要一个更通用的高级函数,它将遍历一个序列并在它找到给定谓词的非零结果时中断——你总是可以编写自己的 find
函数:
extension SequenceType {
func find<T>(@noescape predicate: (Self.Generator.Element) throws -> T?) rethrows -> T? {
for element in self {
if let c = try predicate(element) {return c}
}
return nil
}
}
您现在可以像这样编写 firstAbove100
函数:
func firstAbove100(a:[Int]) -> Int? {
if a.count < 2 {
return nil
}
return (0..<a.count-1).find { i in
a[i]+a[i+1] > 100 ? i : nil
}
}
现在,当它找到一对加起来大于 100 的元素时,它会短路。
或者假设您现在想要 return 元素的总和,而不是 return 数组中加起来大于 100 的第一对元素的索引。你现在可以这样写:
func sumOfFirstAbove100(a:[Int]) -> Int? {
guard a.count > 1 else {return nil}
return (0..<a.count-1).find { i in
let sum = a[i]+a[i+1]
return sum > 100 ? sum : nil
}
}
let a = [10, 20, 30, 40, 50, 60, 70, 80, 90]
print(sumOfFirstAbove100(a)) // prints: Optional(110)
find
函数将遍历数组,将谓词应用于每个元素(在本例中为数组的索引)。如果谓词returns nil
,那么它将继续迭代。如果谓词 return 非零,那么它将 return 结果并停止迭代。
我正在寻找一种在评估部分输入序列后停止更高级别函数的方法。
考虑在满足特定条件的序列中查找第一个索引时的情况。例如,假设我们正在寻找 Int
数组 a
中的第一个位置,其中两个连续值的总和大于 100。
你可以用一个循环来做,像这样:
func firstAbove100(a:[Int]) -> Int? {
if a.count < 2 {
return nil
}
for i in 0..<a.count-1 {
if a[i]+a[i+1] > 100 {
return i
}
}
return nil
}
一旦发现感兴趣的位置,循环就会停止。
我们可以使用 reduce
重写这段代码,如下所示:
func firstAbove100(a:[Int]) -> Int? {
if a.count < 2 {
return nil
}
return (0..<a.count-1).reduce(nil) { prev, i in
prev ?? (a[i]+a[i+1] > 100 ? i : nil)
}
}
但是,这种方法的缺点是 reduce
一直到 a.count-2
,即使它在第一个索引处找到了匹配项。结果是一样的,但能减少不必要的工作就好了。
有没有办法让 reduce
停止尝试进一步的匹配,或者可能有一个不同的函数让你在找到第一个匹配后停止?
indexOf
将在找到第一个匹配项后停止,因此您可以将 firstAbove100
重写为如下内容:
func firstAbove100(a:[Int]) -> Int? {
return a.count > 1 ? (a.startIndex..<a.endIndex-1).indexOf({ a[[=10=]] + a[[=10=] + 1] > 100 }) : nil
}
如前所述,reduce
是专门为评估整个序列而设计的,因此不是为了短路而设计的。以这种方式使用它来查找满足给定谓词的元素的索引最好使用 indexOf
as
另外,从 Swift 3 开始,Sequence
上现在有一个 first(where:)
函数,可以让您找到满足给定谓词的第一个元素。这可能是比 indexOf
更合适的替代方案,因为它 return 是元素而不是索引(尽管在您的特定示例中它们是相同的)。
你可以这样写你的例子:
func firstAbove100(_ a:[Int]) -> Int? {
guard a.count > 1 else {return nil}
return (0..<a.count-1).first { i in
a[i]+a[i+1] > 100
}
}
然而,如果你想要一个更通用的高级函数,它将遍历一个序列并在它找到给定谓词的非零结果时中断——你总是可以编写自己的 find
函数:
extension SequenceType {
func find<T>(@noescape predicate: (Self.Generator.Element) throws -> T?) rethrows -> T? {
for element in self {
if let c = try predicate(element) {return c}
}
return nil
}
}
您现在可以像这样编写 firstAbove100
函数:
func firstAbove100(a:[Int]) -> Int? {
if a.count < 2 {
return nil
}
return (0..<a.count-1).find { i in
a[i]+a[i+1] > 100 ? i : nil
}
}
现在,当它找到一对加起来大于 100 的元素时,它会短路。
或者假设您现在想要 return 元素的总和,而不是 return 数组中加起来大于 100 的第一对元素的索引。你现在可以这样写:
func sumOfFirstAbove100(a:[Int]) -> Int? {
guard a.count > 1 else {return nil}
return (0..<a.count-1).find { i in
let sum = a[i]+a[i+1]
return sum > 100 ? sum : nil
}
}
let a = [10, 20, 30, 40, 50, 60, 70, 80, 90]
print(sumOfFirstAbove100(a)) // prints: Optional(110)
find
函数将遍历数组,将谓词应用于每个元素(在本例中为数组的索引)。如果谓词returns nil
,那么它将继续迭代。如果谓词 return 非零,那么它将 return 结果并停止迭代。