This document provides some tips for writing better and more efficient
S-Lang code.  It is not meant to be a tutorial on S-Lang.

Writing Fast Routines
=====================

  The first thing that one must realize is that S-Lang is an interpreted
  language and runs much slower than a compiled language.  This fact can be
  used to justify a rule of thumb that may be used to gauge the speed of a
  S-Lang expression:

    Rule of Thumb: All single token intrinsic statements and expressions run
		   at the same speed.

  The phrase ``intrinsic statements and expressions'' includes not only
  intrinsic functions but also assignment operations as well as pushing an
  object on the stack.  For example, consider the expression:
  
     n = down (20);
     
   This expression consists of the operation of pushing `20' onto the stack
   followed by calling the intrinsic function `down' and assigning it to
   `n'.  It is easiest to see this using the RPN form:
   
   .  20 down =n
   
   (Remember, all RPN expressions must lie on lines that begin with a
   period.).   From the above rule of thumb, this expression executes at a
   speed of 3 units.
   
   Now consider something more tricky.  Consider the expression:
   
       n = n + 1;
       
   It also executes in 3 units of time.  This is equivalent to:
   
       n++;
       
   However, `n++' is a single token and executes in 1 unit of time!  So, the
   rule is that one should try to use the `++' and `--' forms whenever
   possible.  The same statement is true for the `+=' and `-=' operators.
   The statement:
   
      n += 1;
      
   parses to two tokens:
   
   .  1 +=n;
   
   and executes in 2 units.  Note that 
   
      n++; n++;
      
   also achieves the effect `n = n + 2' but executes in 2 units as well.
   
       * Note:  These special forms are currently limited for integers and
                do not work with variables that represent floating point 
		values.
   
   At this point, it should be clear that the code that executes the fastest
   is the code that contains the least number of tokens.   However, this is
   only partially true and only holds for expressions that do not call other
   S-Lang functions.
   
   As an example, consider the recursive function below:
   
       define fib ();   % definition required for recursion
       
       define fib(n)
       {
          if (n == 0) return(0);
          if (n == 1) return(1);
  
          return fib(n - 1) + fib(n - 2);
       }
       
   The first thing to do is to change
   
         if (n == 0) return 0;
	 
   to 
         !if (n) return 0;
	 
   since the expression `n == 0' executes in 3 units and `n' executes in
   only 1.  Similarly, the statement:
   
         if (n == 1) return 1;
	 
   may be written:
   
         n--; !if (n) return 1;
  
   with the side effect that n is reduced by 1 in the process.  But that is
   a good thing because we can then modify:
   
       return fib (n - 1) + fib (n - 2);
       
   to:
   
       return fib(n) + fib (n - 1);
       
   Actually, we can do better than that.  We can write `n - 1' which
   executes in 3 units to the equivalent expression `n--, n' that executes
   in 2 units to produce:
   
      return fib (n) + fib (n--, n);
      
   Here the fact that `n--' does not affect the stack makes this possible.
   Finally, we are left with:
   
      define fib (n)
      {
        !if (n) return 0;
	n--; !if (n) return 1;
	return fib(n) + fib (n--, n);
      }

   Simply counting tokens (if, !if, fib, n, ==, 1, -, etc...) shows that the
   unoptimized version has 19 whereas the optimized version has 12. Applying
   the rule of thumb to this suggests that the optimized version should run
   19/12 times faster!  The reader is encouraged to compare the speed of the
   first version of `fib' with the optimized version to verify that this is
   indeed the case (use something like fib(25)).
   
   Here is a brief summary of what to do to squeeze extra speed out of
   S-Lang: 
   
      1.  Use the `++', `--', `+=', and `-=' forms wherever possible.
      2.  Eliminate `if (something == 0)' in favor of `!if (something)'
      3.  Avoid `while(1)' and use `forever' instead.
      4.  For simple looping a fixed number of times use `loop'.
      5.  If you have something like:
      
            for (i = imin; i <= imax; i += di) 
	      {
	         ...
	      }
	    
	  replace it by the MUCH faster and smaller version
	  
	    _for (imin, imax, di)
	       {
	         i = ();
		   .
		   .
	       }
	       
      6.  For multiplication by 2 use the `mul2' function.  
      7.  To square a number, do not use `x * x'.  Use `sqr(x)'.
      

Writing Memory Efficient Routines
=================================

   In the previous section, it was emphasized that routines that use the
   least number of tokens are the fastest.  Since they use the smallest
   number of tokens, they take up less space and and are more memory
   efficient.  
   
   Avoid the use of literal strings wherever possible.  Strings take up more
   memory than variables assigned to strings.  For example,
   
     define silly ()
     {
        if (file_exists ("my_file.txt")) 
	  rename ("my_file.txt", "my_file.old");
	else create ("my_file.txt");
     }
     
   should be written:
   
     define silly ()
     {
        variable file = "my_file.txt";
        if (file_exists (file)) 
          rename (file, "my_file.old");
        else create (file);
     }

   Similarly, when working with strings with 1 character, e.g., "a", if
   possible, use routines that work with characters instead.  For example,
   instead of using:
   
      insert ("a");      
      if (looking_at ("a")) ...
      
   use:
   
      insert_char ('a');
      if (looking_at_char ('a')) ...
      
   Finally, never use `""'; use `Null_String' instead.
   
   

