|Scilab Bag Of Tricks: The Scilab-2.5 IAQ (Infrequently Asked Questions)|
|Prev||Chapter 3. Programming Style||Next|
Though not recognized as that by all programmers the flow control structures themselves are first class indicators of the codes workings. We consider three important cases here.
while vs. for,
if vs. select, and
strict block structure vs. premature return.
Expressed in words a for loop tells us:
We know exactly how many iterations we shall need before we start looping.
Nothing in the loop body will change this.
Whereas the while loop says:
We must check whether we should loop at all, and
we have to re-check after each iteration whether we need another round-trip.
Corollary: The termination condition of a while must be influenced in the loop's body.
Compare the next to code snippets, the first calculating the average of a vector of numbers, the second searching zeroes of a given function.
values = [ 1.0 2.0 3.0 4.0 5.0 ]; average = 0; n = size(values, 'c'); // line 3 for i = 1:n average = average + values(i) end; average = average / n
Form line 3 on, we know the number of iterations, n; from the problem we know that nothing will change that. Thus a for-loop is adequate.
deff('[y, dy] = fun(x)', .. 'y = -0.5 + 1.0 / (1.0 + x^2), .. dy = -2.0 * x / (y + 0.5)^2'); x0 = 0.76; [y, dy] = fun(x0); while abs(y) > sqrt(%eps) x = y / dy - x0; x0 = x; [y, dy] = fun(x) end;
Assuming that the function fun and the start guess x0 is given by the user, we do not know how many loops it will take for Newton's algorithm to converge, if it does converge at all. (In the example it does.) Here, the while-loop expresses this lack of a-priori knowledge.
The relationship between if and select bears similarity with while and for respectively. In a select clause the different cases are known – and spelled out explicitely – before the thread of control enters the construct. There is a one to one relationship between the states of the selecting expression and the case branch taken. The else branch in a select works exactly as the else in an if.
Example 3-2. Function fibonacci
function f = fibonacci(n) // return n-th Fibonacci number select n case 0 then f = 1 case 1 then f = 1 else f = fibonacci(n - 1) + fibonacci(n - 2) end
The selecting expression is not restricted to scalars. For example, vectors work too:
Example 3-3. Function shape4
function s = shape4(m) // classify a 2x2 matrix according to its shape select abs(m) <= %eps case [%t %t; .. %t %t] then s = "empty" case [%t %f; .. %f %t] then s = "diagonal" case [%f %f; .. %t %f] then s = "upper triangular" case [%t %t; .. %f %t] then s = "lower triangular" case [%f %f; .. %f %f] then s = "full" else s = "general" end
An if clause is more flexible than a select clause, but at the price of being less expressive. Whenever a whole range of values has to be covered the if clause is the only way to go.
The paradigm of structured programming is: "Every block has one and only one entry point." That's it! Nothing is said about the number of exit points. The purists often misinterpret the paradigm, demanding a single exit point, too. We prefer our freedom and choose whatever we find adequate to the problem.
Here are two different implementations of an algorithm calculating the factorial of a given integral number.
function y = fact_block(x) // faculty of x // block-structured version select x case 0 then y = 1 case 1 then y = 1 else y = x * fact(x - 1) end
The two special cases 0, and 1 are tested separately and the general case is handled in the else branch.
function y = fact_early_ret(x) // faculty of x // early-return version if x >= 0 && x <= 1 then y = 1 return end y = x * fact(x - 1)
This version immediately returns after having treated the special cases, leaving the general case to the "rest" of the function. In this very short function the advantages of the early return are not striking, however they are if there are many special cases to be handled. The "rest" of the function can then concentrate on the core of the problem without being obscured by deeply nested conditionals.