There was an attempt to address this here, but it doesn't cover bindings and where clauses.
for x in 1...3 where x > 3 {
print(x) // this is NOT executed
} else {
print("not found") // this is executed
}
While I don't think else
is clear enough naming there (I might expect else
to run for all x where x <= 3
, or for it to behave like Python, which does something different still), getting what you're asking for is truly gnarly:
`forElse`: do {
var iterator = (1...3).makeIterator()
var next: Int? {
if let x = iterator.next(), x > 3
{ x } else { nil }
}
guard var x = next else {
print("not found") // this is executed
break `forElse`
}
`repeat`: do {
print(x) // this is NOT executed
guard let next else { break `repeat` }
x = next
continue `repeat`
}
}
While this is an interesting solution, AFAICT it doesn't allow an arbitrary placement of break within the loop body, which is unfortunate.
Oh, that was just to illustrate it working. I would never use that nonsense.
Here's the best I could come up with, still too ugly for real use:
@inline( __always )func pyWhile( body:()->Bool, welse:()->() = {})
{
if !body()
{
welse()
}
}
var x = 0
pyWhile{
while x < 5
{
print( x )
if( x >= 3 ) {return true}
x += 1
}
return false
}
welse:
{
print( "Normal exit" )
}
Note this code has to use "return" to break out of the body.
Output without return:
0
1
2
3
4
5
Normal exit
output with return:
0
1
2
3
I wonder if it would be possible to use Swift macros to implement the equivalent of a Python while...else statement? (Haven't had the time to dig deeply into Swift's macro capabilities, so I don't know).
After giving this subject way more thought that it really deserves, I think I've come up with the cleanest solution I can:
var x = 0
outahere:do
{
while x < 5
{
print( x )
//if( x >= 3 ) {break outahere}
x += 1
}
print( "Normal exit" )
}
The only bit of syntactical ugliness is being forced to use a labeled break statement (well, plus an enclosing do statement).
The really nice thing about this approach is that it works for anything, not just while loops:
outahere:do
{
for x in 0..<5
{
print( x )
if( x >= 3 ) {break outahere}
}
print( "Normal exit" )
}
In fact, this scheme also solves a problem I've been complaining about elsewhere: you can use it to force a deinit call when the main program of a console application terminates (by wrapping all the outer-level code with a do{...}):
do
{
class withDeinit
{
init()
{
print( "Creating class" )
}
deinit
{
print( "deinitializing class" )
}
}
let classObject = withDeinit()
} // end do
Output from this last snippet is:
Creating class
deinitializing class
The trick is to wrap the entire outer level code with a do{...} statement and deinit() will be invoked when the do{...} statement goes out of scope.
I have to agree with Danny:
do
's power is underly advertised.