Reputation: 3752
I'd like to create a function that will iterate over an array (or collection or sequence). Then I will call that function with an array, and the reversed version of the array (but efficiently: without creating a new array to hold the reverse).
If I do this:
func doIteration(points: [CGPoint]) {
for p in points {
doSomethingWithPoint(p)
}
// I also need random access to points
doSomethingElseWithPoint(points[points.count-2]) // ignore obvious index error
}
And if I have this:
let points : [CGPoint] = whatever
I can do this just fine:
doIteration(points)
But then if I do this:
doIteration(points.reverse())
I get 'Cannot convert value of type 'ReverseRandomAccessCollection<[CGPoint]> to expected argument type [_]'
Now, I DON'T want to do this:
let reversedPoints : [CGPoint] = points.reverse()
doIteration(reversedPoints)
even though it will work, because that will (correct me if I'm wrong) create a new array, initializing it from the ReverseRandomAccessCollection returned by reverse().
So I guess I'd like to write my doIteration function to take some sort of sequence type, so I can pass in the result of reverse() directly, but ReverseRandomAccessCollection doesn't conform to anything at all. I think I'm missing something - what's the accepted pattern here?
Upvotes: 3
Views: 254
Reputation: 59506
You can do this
let reversedPoints : [CGPoint] = points.reverse()
doIteration(reversedPoints)
or this
doIteration(points.reverse() as [CGPoint])
but I don't think there is any real difference by the point of view of a the footprint.
let reversedPoints : [CGPoint] = points.reverse()
doIteration(reversedPoints)
Infact in this case a new Array containing references to the CGPoint(s)
present in the original array is created. This thanks to the Copy-on-write mechanism that Swift used to manage structures.
So the memory allocated is the following:
points.count * sizeOf(pointer)
On the other hand you can write something like this
doIteration(points.reverse() as [CGPoint])
But are you really saving memory? Let's see.
A temporary variable is created, that variable is available inside the scope of the function doIteration
and requires exactly a pointer for each element contained in points
so again we have:
points.count * sizeOf(pointer)
So I think you can safely choose one of the 2 solutions.
We should remember that Swift manages structures in a very smart way.
When I write
var word = "Hello"
var anotherWord = word
On the first line Swift create a Struct and fill it with the value "Hello".
On the second line Swift detect that there is no real reason to create a copy of the original String so writes inside the anotherWord
a reference to the original value.
Only when word
or anotherWord
is modified Swift really create a copy of the original value.
Upvotes: 0
Reputation: 10091
If you change your parameter's type to a generic, you should get the functionality you need:
func doIteration
<C: CollectionType where C.Index: RandomAccessIndexType, C.Generator.Element == CGPoint>
(points: C) {
for p in points {
doSomethingWithPoint(p)
}
doSomethingElseWithPoint(points[points.endIndex - 2])
}
More importantly, this won't cause a copy of the array to be made. If you look at the type generated by the reverse()
method:
let points: [CGPoint] = []
let reversed = points.reverse() // ReverseRandomAccessCollection<Array<__C.CGPoint>>
doIteration(reversed)
You'll see that it just creates a struct that references the original array, in reverse. (although it does have value-type semantics) And the original function can accept this new collection, because of the correct generic constraints.
Upvotes: 2