Использовать логический оператор как комбинацию закрытия в сокращении

Я пытаюсь уменьшить массив Bool, применяя логический оператор ИЛИ (||), используя следующий код, однако я получаю сообщение об ошибке:

func reduceBools(values: [Bool]) -> Bool {
    return values.reduce(false, combine: ||)
}

Неоднозначная ссылка на член '||'

Аналогично для целых чисел код работает как шарм.

func reduceInts(values: [Int]) -> Int {
    return values.reduce(0, combine: +)
}

Мне удалось заставить его работать, добавив функцию || (код ниже) или используя закрытие { $0 || $1 }, но мне не нравятся эти подходы, и я предпочел бы просто передать оператор.

func ||(lhs: Bool, rhs: Bool) -> Bool {
    return lhs || rhs
}

То же самое происходит для логического оператора И (&&).

Как я могу заставить его работать без использования взлома выше?

Ответ 1

В качестве альтернативы вы можете использовать следующий подход

// ||
func reduceBoolsOr(values: [Bool]) -> Bool {
    return values.contains(true)
}

// &&
func reduceBoolsAnd(values: [Bool]) -> Bool {
    return !values.contains(false)
}

Обратите внимание, что .reduce поставляется с накладными расходами. Если конечным результатом является важность вашего вопроса (а не выяснение выше неожиданного поведения операторов || и && в этом контексте), то, возможно, прагматичный подход выше может помочь, даже если он действительно не уменьшите массив, однако, получив тот же результат из-за простой природы булевого типа.

Ответ 2

Неоднозначная ссылка на член '||' означает, что существует несколько возможных кандидатов, из которых компилятор не может выбрать. В вашем случае это

public func ||<T : BooleanType, U : BooleanType>(lhs: T, @autoclosure rhs: () throws -> U) rethrows -> Bool

и

public func ||<T : BooleanType>(lhs: T, @autoclosure rhs: () throws -> Bool) rethrows -> Bool

Вероятно, ваш "взлом" с помощью { $0 || $1 } является лучшим решением здесь.

Ответ 3

Это происходит из-за семантики закрытия Swifts. Он принимает ваши аргументы и применяет к ним функцию, опуская имена аргументов.

protocol Numeric {
    ...
    public static func +(lhs: Self, rhs: Self) -> Self
    ...
}

В примере с Ints вы передадите (Int, Int) в замыкание, а функция + в числовом протоколе ожидает ровно два ints, чтобы их суммировать.

Вот почему код, как показано ниже, работает просто отлично

[1, 2, 3, 4].reduce(0, +)

Потому что вы просто взяли 2 ints и прикладную функцию, которая занимает всего два ints. Если вы напишете свою собственную функцию, которая будет принимать только два аргумента, она также будет работать.

func myOwnAwesomeFunc<T: Numeric>(a: T, b: T) -> T { in
    return 1 // production ready
}

[1, 2, 3, 4].reduce(0, myOwnAwesomeFunc) // prints 1

Хорошо. Но почему мы не можем писать

[true, false, true].reduce(false, ||) // yields Cannot invoke 'reduce' 
// with an argument list of type 
// '(Bool, (Bool, @autoclosure () throws -> Bool) throws -> Bool)'

Это потому, что этот оператор принимает bool и замыкание, которое возвращает bool. Не bool, закрытие! Но если это так, почему мы не пишем true || { false }()? Это из-за @autoclosure, которая заботится о фигурных скобках для нас.

Основной вопрос, почему он реализован таким образом, поэтому мы не можем использовать Swifts awesome short-hand syntax with booleans? ИДК

Ответ 4

Следующий подход будет работать

values.reduce(false) { $0 || $1 }