Control Flow Statements
An important property of a Programming Language for it to be Turing Complete is for its control flow to be able to branch, or jump to a different part of the program. A branching instruction can be in the form of a conditional (if-then
), a loop, a switch statement, or unconditional statements, like goto
, or subroutine calls.
Contents
If statements
In many high level languages, if
statements or conditionals are used to direct the flow of the control according to a condition evaluated during the execution.
The primary form of an if
statement is the following:
1 2 3 |
|
Here, condition is supposed to be something that can be interpreted as a boolean value, i.e, something which can be interpreted as true or false. The do_something
statements are a part of, what is called the if-block.
An usual extension to the if
is the else
, which marks a block that is executed if and only if (no pun intended) the if
block does not execute, i.e, the condition is not met.
1 2 3 4 |
|
We could want to check through a series of different conditions, using the if
, in which case we usually nest the if
statements.
1 2 3 4 5 6 7 8 |
|
For an indented language like Python, this does look silly, but its just okay for other languages. For example, in C++:
1 2 3 4 5 6 7 8 9 |
|
Luckily for us, there is the elif
statement in Python, which is the shorthand for the same thing.
1 2 3 4 5 6 |
|
Another alternate form of the above is the switch
statement in some languages:
1 2 3 4 5 6 7 8 9 10 11 |
|
The switch statement checks if the value
matches the particular case. Note that we terminate each case with a break
, without which all of the subsequent code would be executed. This is called a fall-through.
FizzBuzz Problem Looping through the numbers from 1 to N, print the number if it is not divisible by 3 or 5, print "Fizz" if it is divisible by 3, "Buzz" if it is divisible by 5, and "FizzBuzz" if by both.
1 2 3 4 5 6 7 8 9for i in range(1,N+1): if i%3==0 and i%5==0: print "FizzBuzz" elif i%3==0: print "Fizz" elif i%5==0: print "Buzz" else: print i
Goto statements
goto
statements are instructions to jump the execution flow to a labeled statement somewhere else in the program.
1 2 3 4goto end; std::cout << "This line is never executed" << std::endl; end: std::cout << "Great, now we are at the end" << std::endl;
In the above example, the program never gets to run line 2.
A good way to use goto
is to jump out of a deeply nested loop.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
To be fair, goto
can be used to replace many other statements including the loops. For example,
1 2 3 4 5 6//print 1 to 10 int i = 1; top: std::cout << i; i++; if (i <= 10) goto top;
However, this is not the best idea because it leads to what is called spaghetti code: a program in which the control flow is difficult to reason through. Thanks to structured programming, we do not need to resort to this practice any more.
Fun Fact: Computer Scientist Dijkstra published an essay GOTO Statement Considered Harmful in the March 1968 Communications of the ACM explaining why the GOTO statment is best avoided. A criticism of the same was published titled 'GOTO Statement Considered Harmful' Considered Harmful. A further reply was titled "GOTO Considered Harmful" Considered Harmful' Considered Harmful?
Loops as Conditionals
Loops like while
and for
can also be viewed as branching statements. It is perfectly possible for a while loop to skip the while
block completely, when the condition is not met. This is equivalent to using an if
.
To be clear, these two programs are equivalent:
1 2 |
|
1 2 3 |
|
Also, as we have already discussed, a loop is equivalent to an if
equipped with a goto
.
Function Calls
See: subroutines
In procedural programming, function calls cause the control flow to jump from the current execution point to the beginnning of the subroutine. Function calls in modern programming languages are a very effective way to orgranize code.
An important point about function calls is that they take up extra memory space. This is because the program needs to store information about where the execution flow should return to once the function is finished executing. Typically, this information is stored in an area of the RAM allocated by the operating system called the stack. Since the size of the stack is usually static, it can be fatal to place too many function calls.
In functional programming languages, the functions are implemented in a similar way in the lower level, although theoretically that is not the correct way to reason about them. In functional programming, functions are supposed to be declarative abstractions, not procedural.
Jump Instuctions in Assembly
This section is incomplete. You can help by editing.
Jump instructions in assembly are much like the goto
in high level languages. They allow the programmer to set the value in the Instruction Pointer register.
They are either unconditional jumps (jmp
), or jumps on equality, inequality, if greater, less, when overflow, etc.