I was reading a section of this book about the switch statement in Go. But this example confused me:
package main
import "fmt"
func main() {
k := 6
switch k {
case 4: fmt.Println("was <= 4"); fallthrough;
case 5: fmt.Println("was <= 5"); fallthrough;
case 6: fmt.Println("was <= 6"); fallthrough;
case 7: fmt.Println("was <= 7"); fallthrough;
case 8: fmt.Println("was <= 8"); fallthrough;
default: fmt.Println("default case")
}
}
The output is:
was <= 6
was <= 7
was <= 8
default case
And the book points out:
use the fallthrough statement to indicate that the case block following the current one has to be executed.
Now I've to questions:
Why does Go compare by default, in which case k is lower?
The text mentions that the following cases are executed. Fine. But why aren't they only executed of the case matches k?
The Go Programming Language Specification
Switch statements
Expression switches
In an expression switch, the switch expression is evaluated and the
case expressions, which need not be constants, are evaluated
left-to-right and top-to-bottom; the first one that equals the switch
expression triggers execution of the statements of the associated
case; the other cases are skipped. If no case matches and there is a
"default" case, its statements are executed. There can be at most one
default case and it may appear anywhere in the "switch" statement. A
missing switch expression is equivalent to the boolean value true.
ExprSwitchStmt = "switch" [ SimpleStmt ";" ] [ Expression ] "{" { ExprCaseClause } "}" .
ExprCaseClause = ExprSwitchCase ":" StatementList .
ExprSwitchCase = "case" ExpressionList | "default" .
In a case or default clause, the last non-empty statement may be a
(possibly labeled) "fallthrough" statement to indicate that control
should flow from the end of this clause to the first statement of the
next clause. Otherwise control flows to the end of the "switch"
statement. A "fallthrough" statement may appear as the last statement
of all but the last clause of an expression switch.
The expression may be preceded by a simple statement, which executes
before the expression is evaluated.
Evaluate case expressions top-to-bottom. case 4 and case 5 are false, case 6 is true. case 7, case 8, and default are false but execute by flowing to the next clause via fallthrough.
Related
package main
import "fmt"
func main() {
var age int
fmt.Scanf("%d", &age)
// Code your switch or if...else-if statement here.
switch age {
case 1:
age <= 14
fmt.Println("Toy Story 4")
case 2:
age <= 18
fmt.Println("The Matrix")
case 3:
age <= 25
fmt.Println("John Wick")
case 4:
age <= 35
fmt.Println("Constantine")
case 5:
age > 35
fmt.Println("Speed")
default:
fmt.Println("Error: Unknown age")
}
}
I get a yellow marker in the IDE for the scanf, flagging an unhandled error. Also, all the cases have the first line flagged red, preventing to compile. The error I get is age <= 14 is evaluated, but not used. The same is true for all statements. I have searched the web and looked at examples, and from what I can see, the code is, as the materials and examples state. Here is a screenshot from Go Land (JetBrains):
Does anybody have an idea? Also, if you answer, please keep in mind, that this is a lesson from a Go course and I have to use scanf. I could change the switch to an else if, which is likely going to fix the issue, but I am kind of curious, about why this is happening and how I can fix it. :)
age <= 14 is not a valid Go expression, though it could be used as part of one. Go is very particular about all values being consumed.
This isn't the correct way to write a case with <= operators.
switch age {
case 1:
age <= 14
fmt.Println("Toy Story 4")
...
}
That case matches if the value of age is 1. You want to use a switch style like this:
switch {
case age <= 14:
fmt.Println("Toy Story 4")
case age <= 18:
...
...
}
To add to what Daniel wrote — basically the switch statement (unless it's a type switch but let's not digress) has two forms:
switch { // note the absense of any expression here
case bool_expr_1:
...
case bool_expr_2:
...
and
switch any_expr {
case value_to_match_1:
...
case value_to_match_2:
...
In the first form, expressions in each case branch are evaluated top-to-bottom, and the first which evaluates to true, "wins"—that is, the code in that branch is executed.
In the second form, the expression is evaluated to produce a value which is then compared exactly with the value in each case branch.
You have tried to sort-of combine both forms, which won't work.
You might have thought that those numbers in the case branches is the order of how the branches should be checked but no, that's not it.
I saw this kind of code in vb6.
Private Enum enmMain
STEP_INIT = 1
STEP_RUN = 2
STEP_SLEEP = 3
STEP_SUSPEND = 4
STEP_ERROR = 5
End Enum
Private mStep As enmMain
Select Case mStep
Case Is <= enmMain.STEP_RUN
'Do something
Case enmMain.STEP_RUN To enmMain.STEP_ERROR
'Do something
I don't understand this:
Case enmMain.STEP_RUN To enmMain.STEP_ERROR
If it goes into that case when it meets this condition:
the latest value is STEP_RUN
current value is STEP_ERROR
How does it work?
I am posting on mobile can't write clean.
It means that the case statement will be satisfied by all values of mStep that are between 2 and 5, inclusive.
So there is an imprecision in code. Because the value STEP_RUN appears in an inclusive test twice (see the <= operator). Which behaviour is intended for STEP_RUN, the first or the second? You need to figure it out by understanding the program's logic.
Well, let's read the manual:
If testexpression matches any Case expressionlist expression, the statements following that Case clause are executed up to the next Case clause, or, for the last clause, up to End Select. Control then passes to the statement following End Select. If testexpression matches an expressionlist expression in more than one Case clause, only the statements following the first match are executed.
Select Case will run the first block that matches, and the criteria you can use to match are much more flexible than those allowed in many other languages. Case Is <= enmMain.STEP_RUN Will run for any value of mStep that is less than or equal to 2, and Case enmMain.STEP_RUN To enmMain.STEP_ERROR would run for any value between 2 and 5 inclusive.
Now it seems like somebody didn't quite understand what that meant, though, or at least wrote it in a confusing way, because for a value of 2 only the first Case would run, since as the section I quoted says only the first match is executed.
So the end result is that first 'Do something will run on values of 2 or less, and the second 'Do something will run on values of 3, 4, or 5.
I am trying to understand the switch/fallthrough keyword. Using fallthrough statement in a switch block means the next block is entered even if the expression doesn't match but this is only valid when there has already been a match right? The code below seems to work that way but I just want to be sure.
package main
import "fmt"
func main() {
integer := 5
switch integer {
case 4:
fmt.Println("integer <= 4")
fallthrough
case 5:
fmt.Println("integer <= 5")
fallthrough
case 6:
fmt.Println("integer <= 6")
fallthrough
case 7:
fmt.Println("integer <= 7")
fallthrough
case 8:
fmt.Println("integer <= 8")
fallthrough
default:
fmt.Println("default case")
}
}
Yes. The fallthrough statement transfers control to the next case clause in an expression switch. The expression on the next case is not considered when transferring control.
The specification says this about the fallthrough statement:
A "fallthrough" statement transfers control to the first statement of the next case clause in an expression "switch" statement. It may be used only as the final non-empty statement in such a clause.
and this about expression switches:
In a case or default clause, the last non-empty statement may be a (possibly labeled) "fallthrough" statement to indicate that control should flow from the end of this clause to the first statement of the next clause. Otherwise control flows to the end of the "switch" statement. A "fallthrough" statement may appear as the last statement of all but the last clause of an expression switch.
That's right. The fallthrough statement causes it to go from the last statement on a case clause to the first statement of the next skipping the later case evaluation. From the spec:
In a case or default clause, the last non-empty statement may be a
(possibly labeled) "fallthrough" statement to indicate that control
should flow from the end of this clause to the first statement of the
next clause. Otherwise control flows to the end of the "switch"
statement. A "fallthrough" statement may appear as the last statement
of all but the last clause of an expression switch.
I have the following code:
package main
import (
"fmt"
)
func main() {
switch {
case 1 == 1:
fmt.Println("1 == 1")
fallthrough
case 2 == 1:
fmt.Println("2 == 1")
}
}
Which prints both lines on the go playground - see example here. I would have expected the fallthrough statement to include evaluation of the next case statement, but this seems not to be the case.
Of course, I can always use a bunch of if statements, so this is not a real impediment, but I am curious what the intention here is, since this seems to me to be a non-obvious result.
Anyone care to explain? For example: in this code, how can I get the 1st and 3rd cases to execute?
Switch is not a bunch of ifs. It's more akin to if {} else if {} construct, but with a couple of twists - namely break and fallthrough. It's not possible to make switch execute first and third cases - a switch does not check each condition, it finds first match and executes it. That's all.
It's primary purpose is to walk through a list of possible values and execute a different code for each value. In fact, in C (where switch statement came from) switch expression can only be of integral type and case values can only be constants that switch expression will be compared too. It's only relatively recently, languages started adding support for strings, boolean expressions etc in switch cases.
As to fallthrough logic it also comes from C. There is no fallthrough operator in C. In C execution falls through into next case (without checking case values) unless break operator encountered. The reason for this design is that sometimes you need to do something special and then do same steps as in another case. So, this design merely allows that. Unfortunately, it's rather rarely useful, so falling through by default was causing more trouble when programmer forgotten to put a break statement in, then actually helping when truly omitted that break intentionally. So, many modern languages change this logic to never fall through by default and to require explicit fallthrough statement if falling through is actually required.
Unfortunately, it's a it hard to come up with a non contrived example of fallthrough being useful that would be short enough to fit into an answer. As I said it's relatively rare. But sometimes you need to write code similar to this:
if x == a || x == b {
if x == a {
// do action a
}
// do action ab
} else if x == c {
// do action c
} else if x == d {
// do action d
}
In fact, I needed code of similar structure quite recently in one of my projects. So, I used switch statement instead. And it looked like this:
switch x {
case a: // do action a
fallthrough
case b: // do action ab
case c: // do action c
case d: // do action d
}
And your switch from the question is functionally equivalent to this:
if 1 == 1 || 2 == 1 {
if 1 == 1 {
fmt.Println("1 == 1")
}
fmt.Println("2 == 1")
}
Presumably, Go's fallthrough behavior is modeled after C, which always worked like this. In C, switch statements are just shorthands for chains of conditional gotos, so your particular example would be compiled as if it was written like:
# Pseudocode
if 1 == 1 goto alpha
if 2 == 1 goto beta
alpha:
fmt.Println("1 == 1")
beta:
fmt.Println("2 == 1")
As you can see, once execution enters the alpha case, it would just keep flowing down, ignoring the beta label (since labels by themselves don't really do anything). The conditional checks have already happened and won't happen again.
Hence, the non-intuitive nature of fallthrough switch statements is simply because switch statements are thinly veiled goto statements.
From the language spec:
A "fallthrough" statement transfers control to the first statement of the next case clause in an expression "switch" statement. It may be used only as the final non-empty statement in such a clause.
That seems to perfectly describe your observed behavior.
I come from a C/Java background where switch cases have a cascading effect unless you put a break statement
switch(index)
{
case 0: doSomething();
case 1: doSomethingElse();
// ...
}
So case 0 cascades to case 1.
Is this the case with VB? Will case 0 cascade to case 1 automatically as above?
Select Case Index
Case 0 ' code
Case 1 ' code
...
End Select
No, the Select/Case in VB6 does not have a "break" statement. In fact, I don't think that "break" is even a reserved word in VB6.
If code in case 0 runs, then it will not cascade to case 1.