for { } else { }

3,111 views
Skip to first unread message

Nick Craig-Wood

unread,
Sep 15, 2012, 9:59:50 AM9/15/12
to golan...@googlegroups.com
I'm missing the for else construct that I used to use in Python (and
FORTH before that).

Eg something like this

found := false
for i := range xs {
if xs[i] == target {
found = true
break
}
}
if !found {
log.Fatal("Couldn�t find it")
}

Would become

for i := range xs {
if xs[i] == target {
break
}
} else {
log.Fatal("Couldn�t find it")
}

The else clause is only executed if the for loop is not broken out of.
Eg http://docs.python.org/reference/compound_stmts.html#for

I think this would be a nice 100% backwards compatible change (as adding
an else clause to a for statement is a syntax error now "syntax error:
unexpected else, expecting semicolon or newline or }")

(Apologies if this has been discussed before - I did search the archives
but didn't find anything!)

--
Nick Craig-Wood <ni...@craig-wood.com> -- http://www.craig-wood.com/nick

Jan Mercl

unread,
Sep 15, 2012, 10:39:28 AM9/15/12
to Nick Craig-Wood, golan...@googlegroups.com
On Sat, Sep 15, 2012 at 3:59 PM, Nick Craig-Wood <ni...@craig-wood.com> wrote:
> found := false
> for i := range xs {
> if xs[i] == target {
> found = true
> break
> }
> }
> if !found {
> log.Fatal("Couldn’t find it")
> }
>
> Would become
>
> for i := range xs {
> if xs[i] == target {
> break
> }
> } else {
> log.Fatal("Couldn’t find it")
> }
>

What about:

for i := 0; xs[i] != target; i++ { // works only for len(xs) != 0
if i == len(xs)-1 {
log.Fatal("Couldn’t find it")
}
}

or for xs of any length:

for i := 0; i < len(xs) && xs[i] != target || i == len(xs); i++ {
if i == len(xs) {
log.Fatal("Couldn’t find it")
}
}

(All code untested)

-j

Sameer Ajmani

unread,
Sep 15, 2012, 10:55:10 AM9/15/12
to Nick Craig-Wood, golan...@googlegroups.com

In my experience, extracting such a loop into a function that returns a boolean instead of break usually improves readability.   Then the else case is simply the code after the loop.

On Sep 15, 2012 10:00 AM, "Nick Craig-Wood" <ni...@craig-wood.com> wrote:
I'm missing the for else construct that I used to use in Python (and
FORTH before that).

Eg something like this

  found := false
  for i := range xs {
      if xs[i] == target {
          found = true
          break
      }
  }
  if !found {
      log.Fatal("Couldn’t find it")

  }

Would become

  for i := range xs {
      if xs[i] == target {
          break
      }
  } else {
      log.Fatal("Couldn’t find it")

  }

The else clause is only executed if the for loop is not broken out of.
Eg http://docs.python.org/reference/compound_stmts.html#for

I think this would be a nice 100% backwards compatible change (as adding
an else clause to a for statement is a syntax error now "syntax error:
unexpected else, expecting semicolon or newline or }")

(Apologies if this has been discussed before - I did search the archives
but didn't find anything!)

--
Nick Craig-Wood <ni...@craig-wood.com> -- http://www.craig-wood.com/nick

--


John Waycott

unread,
Sep 15, 2012, 11:23:43 AM9/15/12
to golan...@googlegroups.com
I've never liked the for...else statement in Python. I think of else as an alternative branch. It would make more sense to me if the else clause is executed if the for loop is not.

Anyway, you could use the dreaded goto in this case:

  for i := range xs { 
      if xs[i] == target { 
          goto Found

      } 
  } 
  log.Fatal("Couldn�t find it") 
Found:
  ...

Michael Jones

unread,
Sep 15, 2012, 12:02:43 PM9/15/12
to John Waycott, golan...@googlegroups.com
This is Zahn's idea from 1974.

If we're going to discuss such things, we should all read (or re-read)...
http://sbel.wisc.edu/Courses/ME964/Literature/knuthProgramming1974.pdf
...as a warmup.
> --
>
>



--
Michael T. Jones | Chief Technology Advocate | m...@google.com | +1
650-335-5765

Kevin Gillette

unread,
Sep 15, 2012, 1:44:21 PM9/15/12
to golan...@googlegroups.com
One problem is that for else goes further, but not far enough. What if you want to do something if the loop _was_ broken out of? I've personally found that to be as common a case as doing something when the loop is done. What about do for loops? There's always something extra to add that would improve expressiveness in a few cases, but increase learning curve and decrease readability in all.  There are plenty of other things we could do to modify the grammar and add "conveniences"...

label:
  switch a {
  case x:
    // something
  case y:
    // something else
  case z:
    a = x
    goto label
  }

// language could be tweaked to make the following equivalent

  switch a {
  case x:
    // something
  case y:
    // something else
  case z:
    a = x
    continue // reevaluate cases
  }

But in any case, as with for else, they at most are only conveniences -- in this hypothetical switch continue case, you'd at most literally save one line of code (and it'd be backwards incompatible). In the for else case, you can always determine if the loop would have been broken out of by inverting the loop condition in a following if statement, taking effectively the same amount of code. If you need to determine that it was actually _not_ out of, it's 2 lines of code setup, plus 1 line per use of `break`, but in these cases, it's much easier to restructure code:

  broken := true
  for condition {
    A()
    if subcond1 {
      B()
      break
    } else if subcond2 {
      C()
      break
    }
  } else {
    // not broken out of
    D()
    broken := false
  }
  if !broken {
    // common code for break cases
    E()
    F()
    G()
  }

  // 19 lines of code for the above

  for {
    if !condition {
      D()
      break
    }
    A()
    if subcond1 {
      B()
      goto commonbreak
    } else if subcond2 {
      C()
      goto commonbreak
    }
    continue
  commonbreak:
    E()
    F()
    G()
    break
  }

  // 20 lines of code for the above

  broken := false
  for !broken || condition {
    A()
    if subcond1 {
      B()
      broken = true
    } else if subcond2 {
      C()
      broken = true
    }
  }
  if broken {
    E()
    F()
    G()
  } else {
    D() // the 'else' in 'for else' case
  }

// 18 lines of code for the above

All 3 of the above are equivalent. The last two are valid Go right now, and for a common enough case (needing to know when it _is_ broken, as well as when not broken), the last one is clearer and shorter than the shortest "reasonable" for else case (you could make it shorter with `for broken := true; condition; {} else {}`, but that's just bad style.

Kevin Gillette

unread,
Sep 15, 2012, 1:47:11 PM9/15/12
to golan...@googlegroups.com
Erm. In the second "for else equivalent" example, all uses of "broken" should probably be read as "notbroken"

Nick Craig-Wood

unread,
Sep 17, 2012, 7:53:44 AM9/17/12
to golan...@googlegroups.com
On 15/09/12 17:02, Michael Jones wrote:
> On Sat, Sep 15, 2012 at 8:23 AM, John Waycott <java...@cox.net> wrote:
>> I've never liked the for...else statement in Python. I think of else as an
>> alternative branch. It would make more sense to me if the else clause is
>> executed if the for loop is not.
>>
>> Anyway, you could use the dreaded goto in this case:
>>
>> for i := range xs {
>> if xs[i] == target {
>> goto Found
>> }
>> }
>> log.Fatal("Couldn't find it")
>> Found:
>> ...

If I look back through the 1000s of lines of C code I wrote I see
exactly that construct quite a lot!

> This is Zahn's idea from 1974.
>
> If we're going to discuss such things, we should all read (or re-read)...
> http://sbel.wisc.edu/Courses/ME964/Literature/knuthProgramming1974.pdf
> ...as a warmup.

Yes you are both spot on - the for {} else {} construct is a replacement
for the above with goto. It can be re-written as Knuth states in his
paper (Example 1), but I think the else clause is the neatest solution,
and one familiar from other languages (Python and FORTH).
Reply all
Reply to author
Forward
0 new messages