18

Is there a way to do something similar to break from a for loop but in array's reduce() function?

E.g. consider I have an array:

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

... and I need to get a cumulative || on them. With a for loop, the following would be possible:

var resultByFor = false

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

... i.e. at the moment we get our first true there is no need to finish the loop as the result will be true anyway.

With reduce(), the following looks quite neat and tidy:

var resultByReduce = flags.reduce(false) { $0 || $1 }

However, with the array given in the example, the for loop body would be executed only 3 times, while reduce() function closure would get triggered full 7 times.

Is there a way to make reduce() to bail out on 3rd iteration as well (just like it can be done in for loop)?

[UPD]

I oversimplified the question. The original problem was more like this:

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)
}

... i.e. I have an array of objects and I want to know if there is at least one of them that has certain method evaluating to true.

4

3 回答 3

9

正如其他人所建议的那样,您可以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
于 2015-04-27T23:10:51.467 回答
3

它在 Swift 标准库中不是开箱即用的,但你可以做到。在我的博客文章中,我描述了我提出的解决方案。在您的情况下,它在调用端看起来像这样:

flags.reduce(false, { $0 || $1 }, until: { $0 })

你可以在那个游乐场试一试

于 2017-04-09T07:47:32.993 回答
0

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

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

没有breakfor 迭代闭包,就像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)
}
于 2022-03-01T20:57:48.223 回答