英文:
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
类型,比如Set
或Dictionary
,它们没有固定的顺序。(请注意,像这样的类型提供了以某种一致的顺序进行迭代,但顺序可能是任意的。)
因此,您的map
,filter
和reduce
函数的调用顺序取决于底层集合的迭代顺序,但在大多数情况下,它将与您预期的顺序相同。
英文:
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 Sequence
s 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 -> Int in
sideChannel.append(i * 10) // ⚠️ Don'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 -> Int in
sideChannel.append(i * 10) // ⚠️ Don'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 🗺️<🗺️>(_: 🗺️) -> [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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论