Does Swift have short-circuiting higher-order functions like Any or All?

巧了我就是萌 提交于 2019-12-01 16:27:09

Sequence (and in particular Collection and Array) has a (short-circuiting) contains(where:) method taking a boolean predicate as argument. For example,

if array.contains(where: { $0 % 2 == 0 })

checks if the array contains any even number.

There is no "all" method, but you can use contains() as well by negating both the predicate and the result. For example,

if !array.contains(where: { $0 % 2 != 0 })

checks if all numbers in the array are even. Of course you can define a custom extension method:

extension Sequence {
    func allSatisfy(_ predicate: (Iterator.Element) -> Bool) -> Bool {
        return !contains(where: { !predicate($0) } )
    }
}

If you want to allow "throwing" predicates in the same way as the contains method then it would be defined as

extension Sequence {
    func allSatisfy(_ predicate: (Iterator.Element) throws -> Bool) rethrows -> Bool {
        return try !contains(where: { try !predicate($0) } )
    }
}

Update: As James Shapiro correctly noticed, an allSatisfy method has been added to the Sequence type in Swift 4.2 (currently in beta), see

(Requires a recent 4.2 developer snapshot.)

One other thing that you can do in Swift that is similar to "short circuiting" in this case is to use the lazy property of a collection, which would change your implementation to something like this:

myObjects.lazy.filter({ !$0.isFulfilled }).first != nil

It's not exactly the same thing you're asking for, but might help provide another option when dealing with these higher-order functions. You can read more about lazy in Apple's docs. As of this edit the docs contain the following:

var lazy: LazyCollection> A view onto this collection that provides lazy implementations of normally eager operations, such as map and filter.

var lazy: LazySequence> A sequence containing the same elements as this sequence, but on which some operations, such as map and filter, are implemented lazily.

If you had all the objects in that array, they should conform to some protocol, which implements the variable isFulfilled... as you can see, you could make these objects confrom to (let's call it fulFilled protocol)... Now you can cast that array into type [FulfilledItem]... Now you can continue as usually

I am pasting code here for your better understanding:

You see, you cannot extend Any or AnyObject, because AnyObject is protocol and cannot be extended (intended by Apple I guess), but you can ,,sublass" the protocol or as you like to call it professionally - Make protocol inheriting from AnyObject...

 protocol FulfilledItem: AnyObject{

    var isFulfilled: Bool {get set}

}

class itemWithTrueValue: FulfilledItem{
    var isFulfilled: Bool = true
}

class itemWithFalseValue: FulfilledItem{
    var isFulfilled: Bool = false
}

var arrayOfFulFilled: [FulfilledItem] = [itemWithFalseValue(),itemWithFalseValue(),itemWithFalseValue(),itemWithFalseValue(),itemWithFalseValue(),itemWithFalseValue()]

  let boolValue =   arrayOfFulFilled.contains(where: {
       $0.isFulfilled == false
    })

Now we've got ourselves a pretty nice looking custom protocol inheriting all Any properties + our beautiful isFulfilled property, which we will handle now as usually...

According to apple docs:

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TypeCasting.html#//apple_ref/doc/uid/TP40014097-CH22-ID342

AnyObject is only for reference types (classes), Any is for both value and reference types, so I guess it is prefered to inherit AnyObject...

Now you cast instead AnyObject into Array the protocol Item FulfilledItem and you will have beautiful solution (don't forget every item to conform to that protocol and set the value...)

Wish happy coding :)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!