IDL 5220 Week 5 - Functions, Procedures, and Debugging



  1. Functions:
    Functions, procedures, and in other languages methods, are all types of self-contained modules that break down large tasks into a series of smaller ones. They simplify programs by making debugging easier, and because they are reusable. They can also be called recursively. Large programs are almost always made up of many levels of methods.
    User defined functions act just like the pre-defined system functions such as sin or alog10. You call a function with one or more parameters, and when the function is done, it executes a RETURN statement that returns a value and control back to the main program. You've already seen many examples of functions in IDL:
    a=SIN(x)
    s=strmid("******long ****** string ****",18,6)
    print,alog10(5.e+3)
    a=tvrd()
  2. Recursion:
    Recursion is a very common and useful technique in program design. Recursion basically means that a method repeatedly calls itself. Recursion is well-defined if each time you recurse you are significantly getting closer to the base case. An example demonstrated recursion best: You want to write a function that will return the highest odd factor of a number. For instance, it will return 15 for 15, 9 for 18, 3 for 24, and 1 for 32. This is clearly a case where recursion will be the best solution:
    function factor2,x
       if x MOD 2 eq 1 then return,x
       return, factor2(x/2)
    end
    Here, you can see the base case is if x MOD 2 eq 1. In this case, the number is odd so it is returned. If the number is even though, it returns factor2(x/2), which will now check if (x/2) MOD 2 eq 1 and if not, keep recursively calling itself. Try typing this program in, and checking print, factor2(24) for instance, or several other numbers. This example is well-defined because each time you recurse, you cut x in half and are thus getting significantly closer to the base case. Both functions and procedures support recursion.
  3. Procedures:
    As with functions, user defined procedures act just like system procedures. You have also seen many system procedures before:
    plot,x,y,title='Plot',psym=4
    strput,s,'Fr.',6
    You have also seen user-defined procedures:
    gatorplot
    fits_read,'file.fits',flux,header
  4. Debugging:
    Debugging is the longest part of programming and always the most frustrating. It is rare that a program works the first try unless it is very short. Functions and Procedures make debugging much easier because you can break down large tasks into a bunch of smaller ones. Each of these smaller tasks is easier to individually debug of course, and once a procedure is working, you don't have to worry about it anymore. For instance in Gatorplot, if I want to plot a smoothed version of a .FITS file, the procedures gatorplot, click, listcheck, readdata, makeplot, fitsplot, and smoothplot are all called. If I didn't have these procedures, my program would be one huge mess, and if there was an error it would be hard to find where it was. As it is, when I added smoothing, all the other procedures worked fine already, so if there was an error it had to be in the procedure smoothplot. The way it is set up, if you click the smooth button, it calls smoothplot, which replaces the array containing the data with an array containing the smoothed data. It then moves onto makeplot as usual.

    Functions and Procedures, on the other hand, do present some problems in IDL in debugging. When a function or procedure exits, all local variables (those not shared with the caller) are destroyed. This makes debugging harder since you don't know the values of your variables when the program crashed. If it is a standard program, you can type print,x or help at the IDL command prompt after a crash and check to see if your variables have the correct values to figure out what went wrong. This necessitates sometimes inserting print and help commands into your procedures and functions at various points before the error happened to determine what went wrong. Actually when debugging a program, you may have to insert these statements as well. You can sometimes find a variable with an incorrect value from the command prompt, but you need to go back and put print statements into your program at various places to check the value of the variable along the way and find out when it became incorrect.

    The STOP procedure will stop execution of a program. You can insert this at any point in your program, and you will be returned to the command prompt there. Then you can check your variables and debug just as you would if the stop were caused by an error.

    IDL is usually good about explaining what error occured, so look at the error message, look at the line number it gave you, and see if you can figure out what went wrong. A common example would be if you create an array of some variable size, and try to access an element number higher than the size of the array. This would return an error like
    % Attempt to subscript A with  is out of range.
    % Execution halted at: $MAIN$            2
    
    Look at line 2 and see what is wrong in this case. If you are using functions and procedures, IDL will give you the line in the main program where the function was called in addition to the line in the function where the error occured, so you can track down if it was an error in that specific call (bad paramater maybe) or an error in the function/procedure in general. This is much better than languages like C++ and FORTRAN where you are not always even given a line number. Also note that because those other languages are compiled and not interpreted, you have no way of doing anything at a command line after an error, so you have to just put in a bunch of print statements to check the values of your variables.

Homework 5: Factorial Function and Linear Interpolation procedure. The project this week consists of 4 tasks:
1. Write a function that uses recursion to calculate a factorial of a number. Even if an integer is input, it should return a floating point number because integers only go up to 32767. It should be called by result=fac(x). My function is only 3 lines.
2. Write a procedure that does a linear interpolation on a 2-D array. The procedure should be called linterp,x,y,z,x2,y2,z2. x and y are 1-D arrays containing the x and y-axis values corresponding to the 2-dimensional array z. x2, y2, and z2 will be calculated by the procedure and returned. x and y being one dimensional arrays are straight forward to interpolate. z should be interpolated like:
0  1      0   .5   1   where the middle point in z2 is calculated by
2  3  =   1   1.5  2   averaging all 4 points in the original array that
          2   2.5  3   surround it.  My procedure is 21 lines long.
3. Write a program that prints the factorial of every number from 0 to 10. It should then create a 5x5 array z and read it in from a file. Copy the file ~warner/IDL5220/contour.dat to your directory and read it into z. x and y can both be findgens of length 5. Do a contour plot of z with the /follow keyword set so it labels every other contour. Call your linear interpolation procedure on x,y, and z. Open a second window and do a contour plot of the interpolated (now 9x9) array with the /follow keyword set. Interpolate this to produce a 17x17 array and do another contour plot in another new window. Notice how the contours get smoother the more interpolations you perform. My program is 14 lines long. You may put the function, procedure, and program all in the same file. You just have to put the program last in the file. So after the end statement for the procedure, just start typing your program, and finish that with another end statement. When you type .run name_HW5.pro, it will compile the function and procedure, but only run the program.
4. Your procedure has to be called with x,y,z. Can you think of a different way to define the procedure that would allow someone to call it with only the 2-dimensional array z and make x and y optional (i.e. you can call the procedure to interpolate z only or x and z or y and z or like now, x, y, and z)?