:orphan:

.. _programming_sli:

Programming in SLI
==================

Overview
--------

A procedure is a sequence of SLI objects whose execution is delayed
until the procedure is executed. Because procedures are objects, they
can be:

-  placed on the stack
-  bound to a name
-  executed repeatedly
-  executed by another procedure

A program is a sequence of SLI objects and procedures which are defined
in a file. This section introduces the fundamentals of SLI programming.

This chapter covers the basic programming concepts of the SLI language
features.

Entering and executing programs
-------------------------------

A program is a sequence of SLI objects and procedures which are defined
in a file. Program files are ordinary ASCII text files, which can be
created and modified with an editor of your choice (e.g. GNU Emacs).

SLI programs usually have the file ending "sli", for example
``hello_world.sli``.

The ``run`` command is used to execute a program file.

Example: "Hello World!"
~~~~~~~~~~~~~~~~~~~~~~~

Write the program ``hello_world.sli`` according to the example, given
above.

1. Create an empty file ``hello_world.sli`` in the directory from which
   the interpreter was started.

2. Copy the *Hello World* example to the file and save it.

3. Enter ``(hello_world.sli) run`` at the command prompt.

   /HelloWorld { (Hello World !) = } def

   SLI ] (hello_world.sli) run SLI ] HelloWorld Hello World !

Note that the procedure is not immediately executed by ``run``. Rather,
all objects which are contained in the file are read and executed.

Using local variables
---------------------

Usually, all names you define are globally accessible. But, if you use a
lot of procedures that define their own variables, there is an
increasing danger that two procedures use the same name for different
purposes. This problem can be solved by keeping variable *local* to the
procedure that defines them.

SLI uses dictionaries to store and resolve variables.

Example 2
~~~~~~~~~

Compute the alpha-function of *t* according to *a(t)=t*Exp(-t/tau)*

::

   /alpha
   {
     << /tau -1.0 >> % create dictionary for local variables
     begin           % open local name space
       /t exch def     % store argument in local variable t
       t tau div exp   % compute formula
       t mul
     end             % close local name space
   } def

Conditionals
------------

Conditional expressions allow a program to ask questions and make
decisions:

-  Comparisons and logical functions

-  Conditional structures which test a certain condition and use the
   result to make a decision.

In general, conditional structures take a *boolean* object as well as
one or more procedure objects as argument and evaluate one of the
procedures, depending on the value of the boolean.

Example 3
~~~~~~~~~

The program in this example implements the faculty function according to
the definition:

::

   fac(1) := 1
   fac(n) := n*fac(n-1), for n>1

The program expects the argument on the stack and replaces it by the
result. Here, we use the ``if`` command to test whether the argument is
greater than 1. The ``if`` command takes two arguments, a boolean and a
procedure object. The boolean is supplied by the ``gt`` command which
test if the object at stack level 1 is greater than the object at level
0.

::

   /fac
   {
     dup    % duplicate the argument, since we still need it.
     1 gt   % If n > 1 we call fac recursively
     {      % according to fac(n)=n*fac(n-1)
       dup
       1 sub fac % call fac with n-1 as argument
       mul       % multiply the result with the argument
     } if
   } def

This example also shows how procedures can be called *recursively*. It
is, however, important to supply a *termination condition* for the
recursion like in this example.

Comparison functions
--------------------

Comparison functions are used to compare objects. The result of
comparison functions are of type ``/booltype`` and can be used for
logical functions and conditional structures.

Command Description ``eq`` Test whether two objects are equal. ``ne``
Test whether two objects are not equal. ``gt`` Test whether the object
at level 1 is greater than the object at level 0. ``lt`` Test whether
the object at level 1 is less than the object at level 0. ``leq`` Test
whether the object at level 1 is less than or equal to the object at
level 0. ``geq`` Test whether the object at level 1 is greater than or
equal to the object at level 0.

Logical functions
-----------------

Command Description ``not`` Negates a bool. ``and`` Returns true if both
arguments are true. ``or`` Returns true if at least one of the arguments
is true. ``xor`` Returns true if and only if one of the arguments is
true.  

The *if-ifelse* structure
-------------------------

Command Description ``bool proc if`` Executes ``proc`` if the boolean is
true. ``bool proc_1 proc_2 ifelse`` Executes ``proc_1`` if the boolean
is true and ``proc_2`` otherwise.

Example
~~~~~~~

::

   SLI ] 1 2 eq {(Equal!) = } { (Not equal !) =} ifelse
   Not equal !
   SLI ] 2 2 eq {(Equal!) = } { (Not equal !) =} ifelse
   Equal!

The *case-switch* structure
---------------------------

While the commands ``if`` and ``ifelse`` test only one condition, the
*case-switch* structure can be used to test a number of different
conditions.

The *case-switch* structure has the general form:

::

   mark
    bool_1 proc_1 case
    bool_2 proc_2 case
           :
    bool_n proc_n case
   switch

In this structure, ``proc_i`` is executed, if the corresponding value of
``bool_i`` is true.

Sometimes it is necessary to provide a default procedure, which is
evaluated if none of the boolean is true.

The *case-switchdefault* structure has the general form

::

   mark
    bool_1 proc_1 case
    bool_2 proc_2 case
           :
    bool_n proc_n case
    procdefault
   switchdefault

Here, ``procdefault`` is executed if none of the booleans was true.

Loops
-----

Loops and control structures are commands that take procedure objects as
arguments.

Infinite loops
~~~~~~~~~~~~~~

The simplest loop is performed by the command ``loop``:

::

   SLI ] {(Hello World) =} loop
   Hello World
   Hello World
   Hello World
   Hello World
   Hello World
   Hello World
   Hello World
   Hello World
      :

``loop`` performs the procedure repeatedly and thus in the example, an
infinite succession of the words "Hello World" is printed. The only way
to leave a ``loop``-structure is to call the command ``exit`` somewhere
inside the loop:

::

   SLI ] 0
   SLI [1] { 1 add dup  (Hello World) = 10 eq {exit} if }
   SLI [2] loop

it prints ten times "Hello World". First the initial value 0 is pushed
on the operand stack. The procedure adds 1 in each cycle and takes care
that one copy of the counter stays on the stack to serve as the initial
value for the next cycle. After the message has been printed, the stop
value 10 is pushed and is compared with the counter. If the counter is
equal to 10, the nested procedure s executed. This procedure then
executes the command ``exit``, and interrupts the loop.

Command Description ``proc loop`` Repeatedly execute procedure ``proc``.
``exit`` Exit the innermost loop structure.

Finite loops
~~~~~~~~~~~~

The last example can be implemented much easier, using a ``repeat``
loop. ``repeat`` takes two arguments: An integer, and a procedure
object. The integer determines how often the procedure is executed.
Thus, in order to print ten times "Hello World" we write:

::

   SLI ] 10 { (Hello World) = } repeat

Sometimes, one needs to know the counter of the loop and one may also be
interested in influencing the step-size of the iterations. For this
purpose SLI offers the ``for``-loop. ``for`` is called like this:

::

   start step stop proc for

``for`` executes the procedure ``proc`` as long as the counter is
smaller than the stop-value (for positive step values) (please refer to
reference *RedBook* for the exact termination conditions).

In each cycle, the current value of the counter is pushed automatically.
This value can be consumed by the procedure. Actually, in very long
running loops, the counter must be removed by the procedure in order to
avoid stack overflow. The following example prints the first ten cubic
numbers:

::

   SLI ] 1 1 10 { dup mul = } for
   1
   4
   9
   16
   25
   36
   49
   64
   81
   100
   SLI ]

Command Description ``n proc repeat`` Execute procedure proc n times.
``i s e proc for`` Execute procedure proc for all values from i to e
with steps. ``array proc forall`` Execute procedure proc for all
elements of ``array``. ``array proc forallindexed`` Execute procedure
proc for all elements of ``array``. ``array proc Map`` Apply ``proc`` to
all elements of ``array``. ``array proc MapIndexed`` Apply ``proc`` to
all elements of ``array``. ``x proc n NestList`` Gives a list of the
results of applying ``proc`` to\ ``x`` 0 through ``n`` times.
