Scilab Bag Of Tricks: The Scilab-2.5.x 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 code's 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 two code snippets, the first one calculating the average value 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.0; n = size(values, 'c'); // line 3 for i = 1:n average = average + values(i); end; average = average / n
From 7nbsp;3 on, we know the number of iterations, n, and 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; x
Assuming that the function fun, and the start guess x0 are supplied 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 to 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.
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, vectors for example work too:
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 = dense' 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, as is demonstrated by Example 3-2.
The paradigm of structured programming is: "Every block has one and only one entry point." That is 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 many special cases are to be handled. The "rest" of the function can then concentrate on the core of the problem without being obscured by deeply nested conditionals.