Jump to content

Lisp EN -- Laboratory 4 -- 2006-2007 -- info.uvt.ro

From Wikiversity
Important! These pages are somehow outdated and it is recommended to consult the newer version at Functional programming -- 2008-2009 -- info.uvt.ro (by Ciprian Crăciun).

Lisp EN -- 2006-2007 -- info.uvt.ro


Input / output

[edit]

It is used to output at the console the representation of its argument.

If used directly in the interpreter we could see the value printed twice:

  • One is the data written to the console by the print function.
  • One is the same data written to the console by the interpretor, as the value resulted from evaluating the given expression, as the return value of print is the printed value.
(print <value>) => <value>
(print 1) => 1
(print '(1 2 3 4)) => (1 2 3 4)
(print "abc") => "abc"


It is used to read and parse the data from the console.

It takes no arguments and reads exactly one object.

By one object we understand a number, string, symbol, list, etc. In a word whatever can be written to the interpretor can be read with this function.

(read) => <parsed-read-value>


Other input / output functions

[edit]


Returning multiple values

[edit]

There are cases when we want to return multiple values from a function.

To obtain this we use the function values which takes any number of arguments and returns a special structure which holds all the given values.

(values <value-1> ... <value-n>) => <value1>; ...; <value-n>
(values 1 2 3) => 1; 2; 3
(defun prod-div (a b)
    (values (* a b) (/ a b)))

(prod-div 1 2) => 2; 1/2
(prod-div 100 5) => 500; 20


Functions returning multiple values

[edit]
(floor 1.5) => 1; 0.5
(round 1.5) => 2; -0.5


If we are using a function which return multiple values (through values) we must use multiple-value-bind form to bind each value to a set of variables.

If we use that function without using multiple-value-bind, only the first value is considered, the rest being ignored.

(multiple-value-bind (<name-1> ... <name-n>)
        <function-call-returning-multiple-values>
    <statement-1>
    ...
    <statement-n>)
=> <statement-n>
(multiple-value-bind (a b)
        (floor 1.5)
    (list a b)) => (1 0.5)


Other multiple value forms

[edit]


Conditional forms

[edit]


Grouping forms

[edit]

progn is used to group multiple statements together. The value returned by this form is the value returned by the last statement.

(progn
    <statement-1>
    ...
    <statement-n>)
=> <statement-n>
(progn
    (setq a 1)
    (setq b 2)
    (+ a b))
=> 3

prog1 and prog2 are similar with progn except that the value returned by each one is the value returned by the first, respectively the second statement.

(prog1
    <statement-1>
    ...
    <statement-n>)
=> <statement-1>

(prog2
    <statement-1>
    <statement-2>
    ...
    <statement-n>)
=> <statement-2>
(prog1 1 2 3 4) => 1
(prog2 1 2 3 4) => 2
(progn 1 2 3 4) => 4


let and let* are used to create a lexical scope in which the given variables are bound to the given initial values.

The value returned by both forms is the value returned by the last statement.

The difference between let and let* is how they execute the initial-value expressions. let executes them in parallel, and let* executes them sequencialy.

(let
       ((<variable-1> <initial-value-1>)
        ...
        (<variable-n> <initial-value-n>))
   <statement-1>
   ...
   <statement-n>)
=> <statement-n>
(let*
       ((<variable-1> <initial-value-1>)
        ...
        (<variable-n> <initial-value-n>))
   <statement-1>
   ...
   <statement-n>)
=> <statement-n>
(setq a "a-is-1")
(setq b "b-is-2")
(print a) => "a-is-1"
(print b) => "b-is-2"

(let
       ((a b))
    (print a) => "b-is-2"
    (print b)) => "b-is-2"

(print a) => "a-is-1"
(setq a "a-is-1")
(setq b "b-is-2")
(let
       ((a b)
        (b a))
   (print a) => "b-is-2"
   (print b)) => "a-is-1"

(let*
       ((a b)
        (b a))
   (print a) => "b-is-2"
   (print b)) => "b-is-2"


Other grouping forms

[edit]


Iterative forms

[edit]

dolist is used to iterate a list (at the first level).

The value returned by the form is the value of the return-value expression. This is optional, and in case it is not given it returns nil.

(dolist (<variable> <list> [return-value])
    <statement-1>
    ...
    <statement-n>)
=> <return-value> or nil

The following examples shows how we can print the contents of a list:

(defun print-list (l)
    (dolist (e l)
        (print e)))

(print-list '(1 2 3 4))

If we want to sum the values in a list we can do:

(defun sum-list (l)
    (let ((sum 0))
        (dolist (e l sum)
            (setq sum (+ sum e)))))

(sum-list '(1 2 3 4)) => 10


dotimes is used to iterate over a sequence starting with 0 until a given upper bound with a step of 1.

Like in the case of dolist the value returned is given by the return-value expression, or if missing nil.

(dotimes (<variable> <upper-bound> [return-value])
    <statement-1>
    ...
    <statement-n>)
=> <return-value> or nil
(dotimes (i 5) (print i)) => 0 1 2 3 4
(dotimes (i 5.1) (print i)) => 0 1 2 3 4 5

Implementing the factorial function with dotimes would be:

(defun n! (n)
    (let ((f 1))
        (dotimes (i n f)
            (setq f (* f (1+ i))))))

(n! 4) => 24
(n! 50) => 30414093201713378043612608166064768844377641568960512000000000000


do and do* are the most complex iterative forms.

Just like let and let*, the difference between do and do* is that do evaluates the initial-value and next-value expressions in parallel, meanwhile do* executes them sequentially.

(do
       ((<variable-1> <initial-value-1> <next-value-1>)
        ...
        (<variable-n> <initial-value-n> <next-value-n>))
       (<termination-condition> <return-value>)
    <statement-1>
    ...
    <statement-n>)

Fibonacci function could be written with do as:

(defun fibonacci (n)
    (do
           ((i 1 (1+ i))
            (a1 0 a2)
            (a2 1 (+ a1 a2)))
           ((= i n) a1)
        (print i)
    )
)

(fibonacci 1) => 0
(fibonacci 2) => 1
(fibonacci 3) => 1
(fibonacci 10) => 34
(fibonacci 100) => 218922995834555169026


Other iterative forms

[edit]

Surgical functions

[edit]

rplaca and rplacd are used to modify the car, respectively cdr of a pair. (A pair is the result of cons.)

The value returned by these functions is the given pair.

These functions can be used to build circular lists.

(rplaca <pair> <new-car-value>) => <pair>
(rplacd <pair> <new-cdr-value>) => <pair>
(setq c (cons 1 2))
(print c) => 1 . 2
(rplaca c 3)
(print c) => 3 . 2
(rplacd c 4)
(print c) => 3 . 4
(setq l '(1 2 3))
(rplaca (car l) l) => ?
(rplacd (cdr l) l) => ?
(rplacd (cddr l) l) => ?


Other surgical functions

[edit]

The following functions are used just like their counterparts, except that they modify the given list instead of creating a new one.

(setq l '(1 2 3 4 5))
(print (reverse l)) => (5 4 3 2 1)
(print l) => (1 2 3 4 5)
(print (nreverse l)) => (5 4 3 2 1)
(print l) => (5 4 3 2 1)


Other modification forms

[edit]


Assignment 4

[edit]

This assignment is due next Wednesday (2007-03-28) at 24:00, and should be sent by email.

When you send the email please include in the subject [LISP-EN-2] First_name Last_name for the second year and [LISP-EN-3] First_name Last_name for the third year. Please paste the code inside the email, DO NOT ATTACH any files.

Write 6 functions:

  • 2 functions using dolist
  • 2 functions using dotimes
  • 2 functions using do or do*

The functions must be followed by 2 or 3 representative test cases.

The functions must be different than the ones presented at the laboratory.

Ciprian Dorin Craciun

2007-03-22

ccraciun@info.uvt.ro