[swift-evolution] For-loop revisited
ted van gaalen
tedvgiosdev at gmail.com
Thu Feb 25 20:11:30 CST 2016
In technical and scientific programming, one often encounters loops
based on iterating and changing numerical values (e.g. floats, integers, doubles)
varying values from positive to negative or reversed with arbitrary
step sizes which also could be positive and negative, and
not bound to or based on collections or arrays..
For example in this function which is part of a Swift/SceneKit app for Apple TV I am
currently constructing:
// generate a matrix of tiles in space:
// ( i don't care how many tiles, i even don't know,
// all i want is a certain area covered with tiles ) :
func addTileMatrix(z z: Float )
{
let w:Float = 20
let h:Float = 5
let l:Float = 5
for var x:Float = -60; x < 60; x += w * 1.2
{
for var y:Float = -30; y < 60; y += h * 1.2
{
let pos = SCNVector3(x: x, y: y, z: z)
let tile = TGStaticTile(pos: pos,
w: CGFloat(w), h: CGFloat(h), l: CGFloat(l),
color: UIColor.randomColor(), naam: "Tile\(tiles.count + 1)")
tiles.append(tile)
}
}
}
As you can see, it relies completely on classical C-style for loops. As I see it, in this case
the classical for-loops in this function provide an easily understood, elegant and compact
way to generate the objects I need.
For reasons not all really convincing me, a lot of us want to drop the C-Style for-loop.
It is by some even considered to be error-prone, really? Something so simple as this?
In this perspective, one could argue that nearly all programming statements are error-prone...
Yes I have read Erica Sadun's proposal SE-0007 "Remove C-Style for-loops with conditions and incrementers."
In this document, the first classical for-loop offered for comparison is a really bad pointer based example,
a typical nightmare C or C++ construct.also: imho the offered alternative with for-in.. alternative is
not really much better. (actually imho Swift should not use (even unsafe) pointers at all ,
but simply pass contiguous byte/word arrays only) In most cases however, the usage of
classical for loops can also be reasonably well structured, as in my example above.
Still, if one wants to get rid of the classical for-loop, (and in many cases I do as well)
then there should be equivalents for iterating with non-collection values, like coordinates as in the above example.
Yes, by deploying the Stride library function, it is possible to convert my example
to the one below without the ancient for loop construct like so, as tested in Playground:
let w:Float = 20 // i need Floats (see the example above)
let h:Float = 5
for var x:Float in (Float(-60.0)).stride(to: Float(60.0), by: w * 1.2)
{
for var y:Float in (Float(-30.0)).stride(to: Float(60.0), by: h * 1.2)
{
print("x = \(x) y = \(y)")
}
}
The above works but imho it is really ugly. Not really an improvement over
the classical for-loop I think.
(btw. in the above example. the compiler (which I think in most cases is superb!)
mistakingly recommends me to change the for-loop vars x and y to 'let'..)
Imho all this casting between floating point types should be done implicitly, but
I've already brought this topic forward here on the forum.
OK, let's clean it up a little: use a single floating point type here (Double):
let w = 20.0
let h = 5.0
for var x in (-60.0).stride(to: 60.0, by: w * 1.2)
{
for var y in (30.0).stride(to: -60.0, by: -h * 1.2)
{
print("x = \(x) y = \(y)")
}
}
(btw: If I don't put () around -60 , then the compiler complains with
"Unary operator '-' cannot be applied to an operand of type 'StrideTo<Double>' "
Could this be a compiler error? Shouldn't it first instantiate or evaluate the
numerical object,before glueing the .stride() to it? )
Although the for loops now have no mixed numerical types, imho, this still looks ugly.
One has to write too much code and it also needs "indirect thought paths"
like when using Objective C.. If I use for-loops, I don't want to think about library functions
like stride (btw nowhere described in the Swift manual !)
Assuming that the Swift for...in.. can only work with a one dimensional array or collection:
then:
Suppose for a moment that one is iterating with very small incrementing or
decrementing values in a relatively large range: Unless the compiler is smart enough
to recognize this, the stride function will allocate and deliver to the for-loop a huge vector
collection or array, with maybe ten thousands of values.. making it all extremely inefficient.
This is of course an unacceptable disadvantage for often used iterating control statements.
To improve this I'd suggest why not simply let the compiler handle the iteration set up internally
like it has been done for almost half a century in most other programming languages?
I would suggest to add these new for loop variants to Swift, as an integral part of the Swift language.
for var n from 10 to 1 by -1 { } // looping backwards
for var n from -10 to 12 // here, the compiler assumes and increment value of 1, no "by" is needed.
for var x: Float from -60.0 to 120 by 0.01 { } // this one has many iterations!
for var d from 0.0 to abyss by -rate {}
I'd suggest also: All literals and vars behind the "from" should whenever possible be
implicitly converted to the loop var's type (var x here)
There's another huge advantage here. Now that the compiler sees a "from" it would
expect 2 or 3 values instead of a (sometimes huge) one dimensional vector.
(it needs to go through each value in the vector, because the vector contens
are unpredictable.)
Range and increment are now well defined, so the compiler is now able to generate
very efficient low level code which would be so much faster
that allocating and iterating through a vector.
As you can see, these for-loop variants are really clean and easy to understand.
Even those with little programming experience will grasp immediately what is
going on here!
Swift was / is also intended to offer an easy entry level for
those new to programming, much easier that starting with Objective C.
So, I'd kindly suggest keep it simple, whenever possible.
Kind Regards
Ted
More information about the swift-evolution
mailing list