为啥 Swift 中的过滤器会迭代集合两次?

Posted

技术标签:

【中文标题】为啥 Swift 中的过滤器会迭代集合两次?【英文标题】:Why does filters in Swift iterate the collection twice?为什么 Swift 中的过滤器会迭代集合两次? 【发布时间】:2014-06-03 21:56:20 【问题描述】:

以下代码在 Swift 的 Playground 或 Console App 中:

let letters = ["A", "B", "C"]

letters.filter(
    (x : String) -> Bool in
    println("PRINT: \(x)")
    return true
)

打印出来:

PRINT: A
PRINT: B
PRINT: C
PRINT: A
PRINT: B
PRINT: C

为什么它会遍历集合两次?

【问题讨论】:

不是说REPL正在打印filter方法的结果吗? 不,它正在执行闭包 6 次 公平地说,filter 的规范并没有指定执行闭包的次数... 这听起来很可能是一个错误。我会告诉苹果。 header里面还有一个全局的filter方法,不过还没想好怎么用…… 【参考方案1】:

很可能filter 被实现为首先计算它需要存储的元素数量,然后在使用该数字确定新数组的存储分配大小后,再次循环以复制他需要保留的元素。

如果你总是返回false,它只会循环一次,这意味着如果结果为空,它会优化第二个循环。

您可能希望将此视为一个错误,但它可能“按设计工作”:毕竟数组不是列表。

【讨论】:

您的回答非常符合事实,我很确定这正是幕后发生的事情。迄今为止最好的回复+1。但是Apple的实现不应该只是分配一个与源数组一样大的结果数组,然后在最后改变它的容量吗?不需要重复两次。 他们可以。他们没有。这是一个实施选择。根据其他实现细节,它可能是也可能不是最佳选择。【参考方案2】:

它在 beta 5 中进行了修改。它现在只运行一次,打印 ABC 而不是 ABCABC

【讨论】:

【参考方案3】:

filter 返回一个正在被 Playground 打印出来的数组。

/// Return a Array containing the elements `x` of `self` for which
/// `includeElement(x)` is `true`
func filter(includeElement: (T) -> Bool) -> T[]

我认为(6 times) 是不正确的,因为如果您在下面查看,true 只会返回 3 次。

编辑:以上内容不正确

从玩多了,我只能说这只是filter函数的行为。

letters.reverse().filter(
    (x : String) -> Bool in
    println("PRINT: \(x)")
    return true
)

这将打印CBACBA,因此它总是按顺序遍历数组两次。

letters.filter(
        (x : String) -> Bool in
        println("PRINT: \(x)")
        if (x == "A") 
           return true
        
        return false
    )

这仍然打印ABCABC,所以去算一下..

稍后我会去问一个 Swift 工程师,然后回复你这是为什么! (如果他们知道:p)

【讨论】:

在控制台应用程序中的结果也是一样的,我更新了示例以显示每个项目调用两次的是 println @mythz 啊,你是对的,我的回答不正确。让我深入研究一下 @mythz 我知道您是对的,因为如果您在前一个下方添加另一个 println,则输出为 AABBCCAABBCC 奇怪的是,如果你总是return false,它每次只运行一次

以上是关于为啥 Swift 中的过滤器会迭代集合两次?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Swift 的 AVPlayer 一次播放会加载 playerItem 两次?

boost::hana: 为啥我不能过滤一个集合?

为啥集合视图中的动态集合单元格不显示为给定大小并且在 swift 3 中滚动后会发生变化

在运行时过滤掉 SQL 数据库中的重复值 - 基于集合

如果你在同一个集合上调用同一个迭代器两次会发生啥?

为啥我不能对相同的数据进行两次迭代?