【问题标题】:Does Swift have short-circuiting higher-order functions like Any or All?Swift 有像 Any 或 All 这样的短路高阶函数吗?
【发布时间】:2017-06-02 20:41:58
【问题描述】:

我知道 Swift 的高阶函数,如 Map、Filter、Reduce 和 FlatMap,但我不知道有任何类似“All”或“Any”的函数在正面测试时返回一个短路的布尔值同时枚举结果。

例如,假设您有一个包含 10,000 个对象的集合,每个对象都有一个名为 isFulfilled 的属性,并且您想查看该集合中是否有任何对象将 isFulfilled 设置为 false。在 C# 中,您可以使用 myObjects.Any(obj -> !obj.isFulfilled),当满足该条件时,它将使枚举的其余部分短路并立即返回 true

在 Swift 中有这样的东西吗?

【问题讨论】:

    标签: swift higher-order-functions short-circuiting


    【解决方案1】:

    Sequence(尤其是CollectionArray)有一个(短路)contains(where:) 方法,将布尔谓词作为参数。例如,

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

    检查数组是否包含 any 偶数。

    没有“全部”方法,但您也可以使用contains() 通过否定谓词和结果。例如,

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

    检查数组中的所有个数是否为偶数。当然你可以定义一个自定义的扩展方法:

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

    如果您想允许“抛出”谓词的方式与 contains 方法,那么它将被定义为

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

    更新:正如 James Shapiro 正确注意到的,allSatisfy 方法已添加到 Swift 4.2 中的 Sequence 类型(目前处于测试阶段),请参阅

    (需要最近的 4.2 开发者快照。)

    【讨论】:

    【解决方案2】:

    在这种情况下,您可以在 Swift 中执行类似于“短路”的另一件事是使用集合的 lazy 属性,这会将您的实现更改为如下所示:

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

    这与您要求的不完全相同,但在处理这些高阶函数时可能有助于提供另一种选择。您可以在 Apple 的文档中阅读有关 lazy 的更多信息。截至本次编辑,文档包含以下内容:

    var lazy: LazyCollection> 这个集合的视图 提供通常急切操作的惰性实现,例如 作为地图和过滤器。

    var lazy: LazySequence> 包含相同的序列 元素作为这个序列,但在其上进行了一些操作,例如map 和过滤器,都是惰性实现的。

    【讨论】:

    • +1 获取有关 Array 上惰性属性的信息。我知道延迟执行直到查询时间的惰性属性,但我不知道数组中的这个特定“视图”。我喜欢!谢谢!
    • 您的链接已失效。介意更新一下吗?更好的是,介意在此处复制相关信息吗?
    • @MarqueIV 已相应更新。
    【解决方案3】:

    如果您拥有该数组中的所有对象,它们应该符合某种协议,该协议实现了变量 isFulfilled... 如您所见,您可以使这些对象符合(我们称之为 fulFilled 协议)...现在您可以将该数组转换为 [FulfilledItem] 类型...现在您可以像往常一样继续

    我在这里粘贴代码以便您更好地理解:

    你看,你不能扩展 Any 或 AnyObject,因为 AnyObject 是协议并且不能扩展(我猜是 Apple 的意图),但是你可以 ,,sublass” 协议或者你喜欢专业地称呼它 - 使协议继承来自任何对象...

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

    现在我们已经有了一个非常漂亮的自定义协议,它继承了所有 Any 属性 + 我们漂亮的 isFulfilled 属性,我们现在将像往常一样处理它......

    根据苹果文档:

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

    AnyObject 只适用于引用类型(类),Any 既适用于值类型,也适用于引用类型,所以我猜还是倾向于继承 AnyObject...

    现在您将 AnyObject 转换为协议 Item FulfilledItem 的数组,您将获得漂亮的解决方案(不要忘记每个项目都符合该协议并设置值...)

    祝编码愉快:)

    【讨论】:

    • 感谢您的回复,但您解决的问题有误。我的问题是询问 Swift 中是否存在短路的高阶函数。我只是用 isFulfilled 作为一个任意的例子。另外,如果我们谈论的是具有 isFulfilled 属性的具体类型的同质序列,那么无论如何您都不会创建协议,因为您只是在对象上使用该属性。我没有说明,但是从技术上讲,您在那里是正确的。尽管如此,具有讽刺意味的是,您的答案包括我所追求的...'包含(在哪里:)所以您确实以迂回的方式回答了它! :)
    猜你喜欢
    • 2016-06-10
    • 1970-01-01
    • 2020-04-29
    • 2021-05-19
    • 2017-05-21
    • 2012-03-03
    • 1970-01-01
    • 1970-01-01
    • 2017-11-30
    相关资源
    最近更新 更多