Chapter 3. Programming Style

Table of Contents
3.1. Spacing and Formatting
3.2. Indentation
3.3. Single Quotes vs. Double Quotes
3.4. Choice Of Control Structures
3.5. Size of a Function

The one and only general guideline to good programming style is: "Make it clear!" And one might extend that to

Make it clear – first of all to you, and then to the poor persons that take over your project (after you have been fired, because of writing illegible code).

Every possible style feature of the language should be used to express the meaning of the code more clearly.

3.1. Spacing and Formatting

Although often underestimated, the format, i.e. the visual layout of the source code itself can greatly help in the understanding of the actions described therein.

3.1.1. Intra-Expression Spacing

We often run into code like this


x=a*c+(x-y)^2*b

This is not bad, especially when typed at the command line for one-time use. However, the expression is not as clear as it could be. It can easily be improved by making the precedence levels (see also Section 4.3) of the operators stand out, as e.g.


x = a*c + b*(x-y)^2

Now, the assignment is intuitively clear at first glance. We use the word "intuitive" here alert the reader of the consequences of incorrectly formatting an expression. Then our intuition will mislead us, as in


x = a * c+(x-y)^2*b

Ouch! This expression is evaluated differently from what it is telling us. We should call it a liar.

See also Section 2.2 for the influence of whitespace on the evaluation of dotted operators.

3.1.2. Line Breaking

Breaking a long expression into lines can improve its readability dramatically. It is particularly recommended for matrix definitions with the square bracket operator. See also Section 2.2.

For example


m1 = [ 1+%i  -1+%i; ..
      -1+%i   1-%i ]

is superior to


m1 = [1+%i -1+%i; -1+%i 1-%i]

If an arithmetic expression is split into lines the operator at which the split occurs always goes onto the next line. Preferred break points occur right before operators of equal precedence.


d2 = fact * (a/(a+d)*(b*(1-delta) + d*delta) - d) * (P./K).^theta

for example becomes


d2 = fact * (a/(a+d)*(b*(1-delta) + d*delta) - d) ..
     * (P./K).^theta

or


d2 = fact ..
     * (a/(a+d)*(b*(1-delta) + d*delta) - d) ..
     * (P./K).^theta

or more dramatic


d2 = fact ..
     * ( ..
            a / (a+d) * (b*(1-delta) + d*delta) ..
            - d ..
       ) ..
     * (P./K).^theta

3.1.3. Setting Brackets Apart

If spaces right inside parentheses or brackets of an expressions make the subexpression stand out more clearly, they should be used. That way


B(k) = a1 * exp(-b1*P(k)/K(k) + b2*Q(k)/K(k))

becomes


B(k) = a1 * exp( -b1*P(k)/K(k) + b2*Q(k)/K(k) )

3.1.4. Vertical Spacing

All previous formatting suggestions of this sections have been concerned with horizontal spacing, and indentation. Vertical spacing is as important as horizontal!

As sentences belonging together go into one paragraph and paragraphs are seperated by one ore more blank lines, Scilab statements that belong together go into one visual block and the blocks should be seperated by single blank lines.


n  = 1;
lo = 1.0 - n*2*epslo();                    // lo = pred(1)
hi = 1.0 + n*  epshi();                    // hi = succ(1)

for k = 2 : 4
    x = lo : (hi - lo)/(k - 1) : hi;
    y = linspace(lo, hi, k);

    disp(size(x, "c") - k);
    disp(x - 1);
    disp(y - 1);
end

If blank lines tear apart blocks of code within functions, it might be preferable to seperate pairs of adjacent functions by at least two blank lines.


function y = baseconv(x, b)
// Convert decimal fraction X into a base-B number.  Each
// "digit" of the result is one element in the result
// vector Y.  To get a monolithic string, apply strcat to Y,
// like
//         strcat(["#", baseconv(x, b), "#", string(b)])
    
    if x >= 0
        y = []
    else
        y = ["-"]
    end
    y = [y, baseconv_integral(int(abs(x)), b)]
    if frac(x) == 0.0
        return
    end

    if y == []  |  y == ["-"]
        y = [y, "0"]
    end
    y = [y, ".", baseconv_frac(frac(abs(x)), b)]


function y = baseconv_integral(x, b)
// Convert decimal integer X >= 0 into a base-B number.  Each
// "digit" of the result is one element in the result
// vector Y.
    if x < 0
        error("integer X (arg 1) out of range; X >= 0.")
    end
    if b < 2  |  b > 36
        error("base B (arg 2) out of range; 2 <= B <= 36.")
    end

    if x == 0
        y = ["0"]
        return
    end

    y = []
    xv = abs(x)
    while xv >= 1
        r = modulo(xv, b)
        if r <= 9
            rs = string(r)
        else
            rs = code2str(r)
        end
        xv = int(xv / b)
        y = [rs, y]
    end


function y = baseconv_frac(x, b)
// Convert decimal fraction 0 < X < 1 into a base-B number.  Each
// "digit" of the result is one element in the result
// vector Y.
    if x <= 0  |  x >= 1
        error("fraction X (arg 1) out of range; 0 < X < 1.")
    end
    if b < 2  |  b > 36
        error("base B (arg 2) out of range; 2 <= B <= 36.")
    end

    y = []
    xv = x * b
    max_mant = prod(2.0 * ones(1, 52))  // 2^52
    n = 1
    while xv > 0  &  n <= max_mant
        r = int(xv)
        if r <= 9
            rs = string(r)
        else
            rs = code2str(r)
        end
        xv = frac(xv) * b
        y = [y, rs]
        n = n * b
    end

    if n >= max_mant
        warning("loss of precision")
    end


function f = frac(x)
// Return the fractional part of X.
// int(X) + frac(X) == X for all X.
    f = x - int(x)