Friday, July 31, 2009

Control structures in emacs lisp

The modern computer allows you to write code that depending on the input, and curent state of the program (or process) dynamically decides what instructions are to be executed next. These type of statements are called control structures and the Gnu emacs reference manual gives a very excellent, easy to understand tutorial about it. It can be accessed here or in emacs by pressing C-h i and then selecting Elisp and then selecting "Control Structures" or you could simply execute the code given below and it will take you directly to the chapter on control structures.
(info "(elisp)Control Structures")

Here, I illustrate a few programs that use control structures.

Sequential Control structures


The sequential control structure allows you to evaluate expressions in the order in which they appear. This is the most common structure and is implicitly included in many forms like in defun, let and so on. Lisp also allows you to explicitly create a sequential construct using functions like progn, prog1 and prog2. progn evaluates the expressions given as arguments to it and returns the value of the last expression. prog1 and prog2 are similar to progn but they return the value of the first and second expressions respectively.
(progn (print "The first form")
       (print "The second form")
       (print "The third form"))

Conditional Control structures


The various conditional structures used in emacs lisp are if, when, unless and cond described here. The conditional structures allow for dynamic selection of which set of instructions to execute.

For instance, the if form is as follows
(if conditional-expr
    (expression for true part)
    body for false part)
The conditional-expr given above must be a boolean expression See here for details on boolean logic in emacs lisp. If conditional-expr returns nil then all expressions in "body for false part" is evaluated else if conditional-expr belongs to any other valid data type like a list or a string or t then only the "expression for true part" is evaluated. If conditional-expr does not evaluate to any valid data type an error is generated. By "body for false part", we mean a series of one or more expressions one after another and the return value is the value of the last expressions. Hopefully, A few examples will make this clearer.

Code :
(if  t
     (message "t evaluates to True")    ; The true part which will be executed and quits
     (message "This is never executed") ; else the first expression is skipped the rest of the
     (+ 1 2)                            ;        expressions executed and the return value is the
     (message "The returned value"))    ;        value of the last expression.

(if  nil
     (message "t evaluates to True")    ; The true part which will be executed and quits
     (message "This is now executed") ; else the first expression is skipped the rest of the
     (+ 1 2)                            ;        expressions executed and the return value is the
     (message "The returned value"))    ;        value of the last expression.

the functions when and unless are special forms of if and they are easy to understand once you get the hang of the if form.

Repetition


Recursion and iteration are two different (but similar) ways of controlling the number of times a set of instructions in your program is executed. Some of the control structures for iteration are while, dolist, dotimes as explained here. Recursion is also suported in emacs lisp.

Let's see an example of the while loop. The code given below is a simple counter that counts from 0 to 9 and prints the numbers in a separate buffer.
Code :
(setq i 0)
(while (< i 10)
           (print i)
           (setq i (+ i 1)))

An example of recursion ? Well, here it is. First, the standard factorial program (Notice the if condition).
(defun fact(n)
        (if (= n 0) 1
        (* n (fact (- n 1)))))
(fact 0)
(fact 4)

Another recursive function, the nth fibonacci number
(defun my-fib(n)
       (if (or (= n 0) (= n 1))
            1
           (+ (my-fib (- n 1)) (my-fib (- n 2)))))
(my-fib 0)
(my-fib 1)
(my-fib 2)
(my-fib 3)
(my-fib 4)


Non-Local exit


This link explains non-local exits. You can also view this in the emacs lisp reference in info.

Code :
(defun foo-outer1 ()
       (catch 'foo
         (foo-inner t)))

    (defun foo-outer2 ()
       (catch 'foo
         (foo-inner nil)))

     (defun foo-inner (b)
       (setq x b)
       (if x
           (throw 'foo "Error thrown"))
        (message "Last line of foo-inner reached"))

     (foo-outer1)
     (foo-outer2)
P.S.:- In case you find the going tough and would like a more detailed exposition, do add your comments and I will consider elaborating because I am not aware of people who are interested and find it difficult to understand the basics.

0 comments:

Post a Comment