是否有可能缩短对更高级别功能的评估?

时间:2016-05-24 13:24:22

标签: swift sequence

我正在寻找一种方法来在评估部分输入序列后停止更高级别的功能。

当您在满足特定条件的序列中查找第一个索引时,请考虑这种情况。例如,我们假设我们正在查找a个数组Int中的第一个位置,其中两个连续值的总和大于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停止尝试进一步的匹配,或者可能是一个让你在找到第一场比赛后停止的不同功能?

2 个答案:

答案 0 :(得分:4)

indexOf会在找到第一个匹配后停止,因此您可能会将firstAbove100重写为以下内容:

func firstAbove100(a:[Int]) -> Int? {
    return a.count > 1 ? (a.startIndex..<a.endIndex-1).indexOf({ a[$0] + a[$0 + 1] > 100 }) : nil
}

答案 1 :(得分:4)

如前所述,reduce是专门为评估整个序列而设计的,因此不会设计为短路。以这种方式使用它来查找满足给定谓词的元素的索引最好使用indexOf作为@Casey says

同样在Swift 3中,first(where:)上有一个Sequence函数,它允许您找到满足给定谓词的第一个元素。这可能是比indexOf更合适的替代方法,因为它返回元素而不是索引(尽管在您的特定示例中它们是相同的)。

你可以像这样编写你的例子:

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以上的元素时,它现在会短路。

或者让我们说,而不是返回数组中添加到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函数将遍历数组,将谓词应用于每个元素(在本例中为数组的索引)。如果谓词返回nil,那么它将继续迭代。如果谓词返回非nil,那么它将返回该结果并停止迭代。