Swift playground 显示错误的语句执行次数(forEach on array)

Swift playground shows wrong number of executions for statement (forEach on array)

我一直在探索仿函数,但我在理解 forEach 仿函数在幕后所做的事情时遇到了一些困难。例如,当我将其输入操场时:

let array = [1] // [1]
array.forEach { [=11=].value } // (3 times)
array.forEach { _ in print("hello") } // (2 times)

当我展开 (3 times)(2 times) 时,它只显示 ()

其一,为什么对 1 个元素的数组执行多次,为什么两次 forEach 计算的执行次数不同?

您在 Swift playground 的右栏中看到的不是该特定行的执行次数,而是该行中给出可显示在该栏中的结果的语句的数量。

array.forEach { [=12=].value } 给出 (3 times) 因为它解析为三个结果:第一个为 array,第二个和第三个为 [=15=].value.

将此行更改为:

array.forEach { \ [1]
    [=10=]          \ (2 times)
}

[=16=] 给出了两个结果。第一个结果通过评估 [=16=] 给出,第二个结果通过评估隐式 return () 语句给出。让我们明确一点:

array.forEach {  // [1]
    [=11=]           // 1
    return ()    // ()
}

当您展开 (N times) 结果游乐场时,应该会向您显示该行中所有语句的结果,但它会出现错误。有时它会显示所有结果或仅显示最后一个结果或仅显示第一个结果或没有结果。我不知道为什么。

这是一个相当混乱的情况。

让我们先考虑第二个forEach

array.forEach { _ in print("hello") } // (2 times)

该行的不同部分在不同的时间执行,Swift 将这些时间中的每一个都计为单独的执行。第一次是在调用array.forEach的时候,第二次是在调用forEach的时候,在匿名函数体中执行对print的调用。如果我们放入换行符,我们可以看到 Swift 每行只执行一次并报告其“值”:

array.forEach { _ in  // [1]
    print("hello")    // ()
}

我们也可以尝试将匿名函数放在一个变量中:

let p: (Int) -> () = { _ in print("hello") } // (2 times)
array.forEach(p)                             // [1]

上面,Swift执行了let p行的一部分来创建匿名函数并将其存储在p中,稍后该行的另一部分调用[=21] =] 在函数体内。

Swift 报告 print 行的值为 () 因为 forEachs 参数必须是 returns [=26] 的函数=](空元组,又名 Void)。由于 print 已经 returns (),Swift 只是让它成为该行的值。

在我们回过头来考虑您的第一个 forEach 示例之前,让我们考虑另一个示例:

print("hello"); print("goodbye") // (2 times)

Swift 表示此行执行了两次,因为该行中的每个单独的语句都算作一次单独的执行。

那么现在让我们考虑你的第一个例子:

array.forEach { [=14=].value } // (3 times)

让我们尝试换行:

array.forEach {  // [1]
    [=15=].value     // (2 times)
}

好的,所以 forEach 调用本身算作一次执行,正如预期的那样。但是 Swift 声称它正在执行两次匿名函数的主体。为什么?

回想一下 forEach 的参数必须是 return 的函数 ()。但是[=36=].value的类型不是();它是内部类型 Builtin.Int64。所以 Swift 在行尾插入另一个语句,到 return ()。实际上,Swift 就像你这样写:

array.forEach {    // [1]
    [=16=].value; ()   // (2 times)
}

我们可以通过在函数中显式添加另一行来证明:

array.forEach {  // [1]
    [=17=].value     // <<<opaque type>>>
    ()
}

现在 Swift 认为每一行都按预期执行了一次。