在Swift中,有没有一种方法可以从数组的reduce函数中退出?

25

在数组的reduce()函数中是否有一种类似于for循环中的break的方法?

例如,考虑一个数组:

var flags = [false, false, true, false, false, true, false]

...并且我需要对它们进行累积 ||。使用 for 循环,下面的方法是可行的:

var resultByFor = false

for flag in flags {
    if flag {
        resultByFor = true
        break
    }
}

...也就是说,一旦我们第一次得到true结果,就没有必要继续循环了,因为最终结果无论如何都将是true

使用reduce(),以下代码看起来相当简洁整洁:

var resultByReduce = flags.reduce(false) { $0 || $1 }
然而,对于给定的数组,在示例中,for循环体只会执行3次,而reduce()函数闭包将被完整地触发7次。

是否有办法让reduce()在第三次迭代时也停止(就像在for循环中可以做的那样)?

[更新]

我过于简化了问题。原始问题更像是这样的:

extension Int {
    func isWholeMultiplesOf(base: Int) -> Bool {
        return (self % base) == 0
    }
}

var numbers = [3, 5, 6, 7, 2, 3, 8]

var resultByFor = false

// The loop body will be triggered only 3 times
for number in numbers {
    if number.isWholeMultiplesOf(2) {
        resultByFor = true
        break
    }
}

// The closure of reduce() will be triggered 7 times
var resultByReduce = numbers.reduce(false) {
    $0 || $1.isWholeMultiplesOf(2)
}

我有一个对象数组,想知道其中是否至少有一个对象具有某个方法并且返回值为true


我不这么认为。reducecontainsfilter 一样会迭代所有的项。我认为第一个例子是正确的。 - Maxim Shoustin
你可以将其编写为扩展。这看起来像是reduce,但可以提前中断。 - qwerty_so
3
为什么不直接使用“contains”? - Fogmeister
5
一般来说,如果你需要打破 reduce 函数的限制,那么就不应该使用 reduce 函数。与其费力避免使用 reduce 函数,不如直接使用 for 循环,这样更好。如果你不喜欢在代码中看到 for 循环,可以将其封装在一个名为 contains 或 find 的通用函数中,在循环体中使用早期返回(early return)。 - Airspeed Velocity
3个回答

12

正如其他人建议的那样,您可以使用contains来实现此目的:

var flags = [false, false, true, false, false, true, false]
contains(flags, true) //--> true

另一个选择是使用 find 查找你要查找的第一个实例,例如这个例子中的 true

var flags = [false, false, true, false, false, true, false]    
find(flags, true) // --> 2, returns nil if not found
let containsTrue = (find(flags, true) != nil)

编辑:Swift的新版本将这些函数暴露在相关的集合协议上,而不是全局函数。

var flags = [false, false, true, false, false, true, false]
flags.contains(where: { $0 == true })
flags.contains(true) // if checking for a specific element
let index = flags.firstIndex(where: ${ $0 == true }) // --> 2

那不是新的等效语法。你不需要为 Bool 序列使用闭包。BoolEquatableflags.contains(true) - user652038
是的,没错,但原问题似乎是简化了使用情况,实际上他们对提供该块很感兴趣,至少根据所做的编辑来看。我会更新我的内容。 - Eric Amorde

4
在Swift标准库中,这项功能并不可用,但是你可以自己实现。在我的博客文章中,我已经描述了我的解决方案。在你的情况下,它会在调用时看起来像这样: flags.reduce(false, { $0 || $1 }, until: { $0 }) 你可以在那个Playground中试一试。

请在此处包含您博客文章的相关内容,因为 Stack Overflow 政策要求如此。这将有助于人们如果您的网站无限期离线。 - idmean
给你,@idmean。 - Maciek Czarnik

0

解决您的“原始问题”的现代方式是

[3, 5, 6, 7, 2, 3, 8].contains {
  $0.isMultiple(of: 2)
}

迭代闭包中没有break,就像在函数主体中一样不允许使用break。为了模拟它,您需要抛出一个错误。

for number in numbers {
  guard !number.isMultiple(of: 2) else {
    break
  }

  print(number)
}

try? numbers.forEach {
  guard !$0.isMultiple(of: 2) else {
    struct Error: Swift.Error { }
    throw Error()
  }

  print($0)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接