Sunday, October 2, 2011

Writing a Scilab Function

Learn how to go about writing your own function in Scilab programming language.

Like Stephen Covey said, "All things we do are created twice, first in our mind and then in reality," at least in spirit if not in those exact words. It applies to programming too. Before jumping in front of a computer to start typing out the function, think about why you need it and what you want it to do.

Perhaps there is already a function in the Scilab library that does what you want your function to do. Or there is an existing Scilab toolbox that has the function you intend to develop.

If you are sure you want to develop your own Scilab function, and have clearly answered the two questions:
  • Why do you want to write the Scilab function you have in mind?
  • What do you want the function to do when it is completed?
Then the next question to ask is:
  • How should the function do what you intend it do do?
To find the answer to that question, ask yourself:
  • What input data does my function need in order to do what it is intended to do? When defining the function, the input to the function are called input parameters. When a function is called, the data input to the function are called input arguments.
  • What should be the data type and name of each input parameter?
  • What should be the order in which the input parameters be listed while defining the function? This is important because this order determines the order in which you supply the input arguments when you call the function. It is a good idea to put most important input data that will always need to be defined (and cannot be substituted by some default value in case the input argument is not supplied when the function is called)
  • What output data does my function return in order to do what it intends to do? A function must return at least one result. It can return more than one result if you want. When defining the function, the output from the function is/are called output parameters. When the function is called, the result(s) that the function returns is/are called output arguments.
  • What should be the data type and name of each output parameter?
  • What should be the order in which the output parameters are listed in the function definition? The order is important because the first output parameter is the default result that can be used as a part of an expression. The remaining output parameters, if any, cannot be used as part of an expression and can be retrieved only if you supply an output argument while calling the function.
When you are convinced that you have convincing answers to each of the above questions, it is time to ask the next question:
  • What is the procedure to be used to arrive at the results based on the input? When you try to answer this question, you may discover one or more of the following:
    • That you forgot to list an input data that was required. Include it in the list and decide its order within the list.
    • That you defined an input data that was never used in the function. Delete it from the list or examine your algorithm to be sure that it can be safely deleted.
    • That you forgot to list an output result that is important. Include it in the list.
    • That you defined an output parameter that was never computed inside the function. Either compute it or delete it from the output parameter list.
The answer to the above question is the algorithm your function uses. Having first created the function in your mind, you are now ready to write and test this function. Note that, the mental process up to now is fairly independent of the programming language in which you intend to develop this function. Learning this skill is an important first step to becoming a competent programmer. What you actually did is, you defined the interface to the function you intend to develop. It consists of the following:
  • Name of the function, preferably reflecting its purpose.
  • Input parameters
  • Output parameter(s)
Writing the function in Scilab
This is specific to the Scilab programming language and will be different when you choose different programming language. Let us begin with the template for a Scilab function:

function [op1, op2, ...] = funcname(ip1, ip2, ...)
  statement1
  statement2
  ...
endfunction

Let us step through this template:
  1. On line 1:
    1. function is a keyword, and needs to be typed exactly as shown.
    2. op1, op2 etc are output parameters which can be any valid Scilab identifier. There must be at least one, but you can have as many as you want. All output parameters are enclosed within square brackets, and separated by commas in case there are more than one.
    3. Assignment operator (=) must be typed exactly as shown
    4. funcname is any valid identifier and serves as the name of the function. In Scilab, rules for choosing identifiers is similar to that in C:
      1. An identifier can consist of any alphanumeric character
      2. First character must be an alphabet, and 
      3. Underscore (_) is treated as an alphabet (even though it is not one).
    5. ip1ip2 etc are output parameters which can be any valid Scilab identifier. There can be zero or more input parameters. All input parameters are enclosed within parentheses, and separated by commas in case there are more than one.
  2. On the remaining lines except the last one, you can use any valid Scilab statement. The statements in use the following variables:
    1. Any input parameter. Input parameters appear on the right hand side of an assignment statement. If a variable appears on the left hand side of an assignment statement, its value is changed but this change is visible only within the function and is not returned to the parent function.
    2. Any output parameter. Output parameters appear on the left hand side of an assignment statement. An output parameter may appear on the right hand side of an assignment statement only after it has been computed (by putting it on the left hand side of an assignment statement) first.
    3. Any local variables defined within the function, but only after they have been initialized first.
  3. Last line:
    1. Last line of a function contains the keyword endfunction. This is a single word and there is no space separating end and function.
An example is worth a thousand words of explanation, quite literally. So here is an example, starting with the interface:
  • Name: linelen. Purpose of the function is to calculate the length of a line in 3D space given the coordinates of its two end points.
  • Input Parameters: p1 and p2, the coordinates of its end points. p1 and p2 are vectors containing either 1, 2 or 3 elements subject to the condition that the number of elements is the same in p1 and p2. Thus, the function can calculate the length of a line in either 1D, 2D or 3D.
  • Output Parameter: L, the length of the line.
Let us discuss the algorithm. The length of a line is the square root of the sum of squares of its projections along the coordinate axes. The projections of a line along the coordinate axes are obtained by subtracting the coordinate of the second point from the corresponding coordinate of the first point. These projections must then be individually squared, added and the square root of the sum determined.

Her is the Scilab function based on the above discussion:

function [L] = linelen(p1, p2)
  delta = p2 - p1
  sum = delta.^2
  L = sqrt(sum)
endfunction

A programmer with some experience with Scilab could easily shorten it to the following code, but it is better to write it as above for a beginner to understand.

function [L] = linelen(p1, p2)
  L = sqrt(sum((p2 - p1).^2))
endfunction

The second version uses the Scilab function sum() to calculate the sum of he elements of a matrix. It dispenses with the need to store intermediate results in local variables as they are not required subsequently. The parentheses surrounding p2 - p1 are important to first calculate p2 - p1 before calculating the element-wise square of the difference.

Having written the function, it is now time to test the function. Let us prepare ourselves for the test by solving a specific numerical example. Let p1 = [0 0 0] and p2 = [3 4 5]. The projections are therefore 3, 4 and 5. The square of the projections are 9, 16 and 25. Sum of the projections is 50. Length of the line is therefore 7.071067812.

We are now ready to test:

-->p1 = [0 0 0]; p2 = [3 4 5];
-->L = linelen(p1, p2)
L  =
    7.0710678

If your function returns a value other than that shown above, it is time to roll up your sleeves and get down to debugging the function. This is detective work, to find where the function went wrong. Best way to achieve is to print out the results at intermediate stages using Scilab's disp() function and calling the function again. Deciding which values to print out can save you lot of debugging time.

Some points to note
  • Names of input/output arguments need not be the same as those of the corresponding input/output parameters although it is acceptable to do so.
    • The function could be called in the following ways:
      • s1 = [0 0 0]; s2 = [3 4 5]; L1 = linelen(s1, s2)
      • len = linelen([0 0 0], [3 4 5])
  • Choose the name for your function carefully. For example, calling our function length() would overwrite an existing Scilab function of the same name which calculates the number of elements in a matrix. When you do this, the built-in Scilab function becomes inaccessible, until you remove your function with the same name from Scilab workspace.

No comments:

Post a Comment

Your comments are ciritical input for the sustainance and improvement of this blog.