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

老子叫甜甜 提交于 2019-12-19 16:58:24

问题


I'm aware of Swift's higher-order functions like Map, Filter, Reduce and FlatMap, but I'm not aware of any like 'All' or 'Any' which return a boolean that short-circuit on a positive test while enumerating the results.

For instance, consider you having a collection of 10,000 objects, each with a property called isFulfilled and you want to see if any in that collection have isFulfilled set to false. In C#, you could use myObjects.Any(obj -> !obj.isFulfilled) and when that condition was hit, it would short-circuit the rest of the enumeration and immediately return true.

Is there any such thing in Swift?


回答1:


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

  • SE-0027 Add an allSatisfy algorithm to Sequence

(Requires a recent 4.2 developer snapshot.)




回答2:


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.




回答3:


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



来源:https://stackoverflow.com/questions/44337137/does-swift-have-short-circuiting-higher-order-functions-like-any-or-all

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