高阶函数在Swift中是否尊重顺序?

huangapple go评论68阅读模式
英文:

Do higher order functions in Swift respect the order?

问题

高阶函数如.map(), .filter(), .reduce() 是否会保留给定集合的顺序?

假设我们有一个整数数组如下:

let arrayOfIntegers: [Int] = [1, 2, 3, 4, 5]

这行代码是否总是返回相同的顺序?

arrayOfIntegers.map { $0 * 2 } // [2, 4, 6, 8, 10]

是否有可能得到类似[2, 6, 10, 4, 8]的结果?

英文:

Not a coding question but it's something that I couldn't find an answer to.

Do higher order functions like .map(), .filter(), .reduce() respect the order of given collection?

Let's assume that we have an array of integers as follows:

let arrayOfIntegers: [Int] = [1, 2, 3, 4, 5]

Will this line of code always return the same order?

arrayOfIntegers.map { $0 * 2 } // [2, 4, 6, 8, 10]

Is it even possible to have something like [2, 6, 10, 4, 8]?

答案1

得分: 2

map(_:), filter(_:), reduce(_:_:)等许多类似的高阶函数都由Sequence协议提供。该协议的目的是定义一种迭代数据序列的方式,并提供了各种方法,这些方法都是基于对数据的顺序访问来实现的。

Sequence提供数据的顺序取决于它自己。虽然许多类型,如Array,具有自然的顺序,但还有其他Sequence类型,比如SetDictionary,它们没有固定的顺序。(请注意,像这样的类型提供了以某种一致的顺序进行迭代,但顺序可能是任意的。)

因此,您的mapfilterreduce函数的调用顺序取决于底层集合的迭代顺序,但在大多数情况下,它将与您预期的顺序相同。

英文:

map(_:), filter(_:), reduce(_:_:), and many other similar higher-order functions are all provided by the Sequence protocol. The purpose of this protocol is for defining a way to iterate through sequences of data, and it provides a wide variety of methods which are implemented in terms of that sequential access through the data.

The order in which a Sequence provides its data is up to it. While many types like Array have a natural order to them, there are other Sequences which do not, like Set or Dictionary. (Note that types like this offer iteration in some consistent order, but the order may be arbitrary.)

The order, then, that your map, filter, and reduce functions will be called in depends on the iteration order of the underlying collection — but in most cases, it will simply be what you expect.

答案2

得分: 2

If you have this code:

let arrayOfIntegers = [1, 2, 3, 4, 5]
let result = arrayOfIntegers.map { $0 * 2 }
print(result) // [2, 4, 6, 8, 10]

The result is guaranteed<sup>* kind of</sup> to always be in that order, for the case of Array.map(_:)

What's not guaranteed is that the calls to the closure will happen in any particular order. So if the closure has any side-effects, they might observe a different order. So don't do this:

let arrayOfIntegers = [1, 2, 3, 4, 5]

var sideChannel = [Int]()

let result = arrayOfIntegers.map { i -&gt; Int in
    sideChannel.append(i * 10) // ⚠️ Don&#39;t put side effects in calls to `map` like this.
    return i * 2
}
print(result) // [2, 4, 6, 8, 10]
print(sideChannel) // [10, 20, 30, 40, 50], but the order is not guaranteed

That's mostly a theoretical concern, however. It's very unlikely to change. Still, don't rely on it.

* <sup>The example in the docs implies the ordering is guaranteed, but I don't see any explicit language guaranteeing the 1-to-1 correspondence between input and output elements. However, changing the ordering would be an absolute shitshow, because of all the existing code that depend on it.</sup>

英文:

To supplement Itai's existing answer:

If you have this code:

let arrayOfIntegers = [1, 2, 3, 4, 5]
let result = arrayOfIntegers.map { $0 * 2 }
print(result) // [2, 4, 6, 8, 10]

The result is guaranteed<sup>* kind of</sup> to always be in that order, for the case of Array.map(_:)

What's not guaranteed is that the calls to the closure will happen in any particular order. So if the closure has any side-effects, they might observe a different order. So don't do this:

let arrayOfIntegers = [1, 2, 3, 4, 5]

var sideChannel = [Int]()

let result = arrayOfIntegers.map { i -&gt; Int in
    sideChannel.append(i * 10) // ⚠️ Don&#39;t put side effects in calls to `map` like this.
    return i * 2
}
print(result) // [2, 4, 6, 8, 10]
print(sideChannel) // [10, 20, 30, 40, 50], but the order is not guaranteed

That's mostly a theoretical concern, however. It's very unlikely to change. Still, don't rely on it.

* <sup>The example in the docs implies the ordering is guaranteed, but I don't see any explicit language guaranteeing the 1-to-1 correspondence between input and output elements. However, changing the ordering would be an absolute shitshow, because of all the existing code that depend on it.</sup>

答案3

得分: 1

Other people are telling what the situation is, for unordered collections, but there are no type-safe guarantees regardless of the orderliness of the sequence. It just happens that everybody writes things* in a way that preserves order coming from the iterator, because it's useful and expected.

Here's an example, where the parameter is totally disregarded, and the output's order is not deterministic. The language provides the freedom for this.

extension Collection {
  func ⭐️⬅️⭐️➡️(_: ⭐️) -> [Element] {
    indices.shuffled().map { self[$0] }
  }
}
(1...5).⭐️("🌴")
(1...5).⭐️ { }
(1...5).⭐️ { $0 * 2 }

* Note: this breaks down for async sequences. The standard there is not to preserve order, unless the work is being done sequentially—parallelism requires more effort.

英文:

Other people are telling what the situation is, for unordered collections, but there are no type-safe guarantees regardless of the orderliness of the sequence. It just happens that everybody writes things* in a way that preserves order coming from the iterator, because it's useful and expected.

Here's an example, where the parameter is totally disregarded, and the output's order is not deterministic. The language provides the freedom for this.

extension Collection {
  func &#128506;&lt;&#128506;&gt;(_: &#128506;) -&gt; [Element] {
    indices.shuffled().map { self[$0] }
  }
}
(1...5).&#128506;(&quot;&#129764;&quot;)
(1...5).&#128506; { }
(1...5).&#128506; { $0 * 2 }

* Note: this breaks down for async sequences. The standard there is not to preserve order, unless the work is being done sequentially—parallelism requires more effort.

huangapple
  • 本文由 发表于 2023年3月4日 05:24:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/75632001.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定