1. Getting Started: A Quick Tutorial |
By following through the subsections of this section, and trying out the code examples and commands given, the users will be well on their way towards using Lush.
1.0. Installation on Linux/UNIX |
Compilation and installation of Lush requires certain packages and libraries be available on your system. In particular, you need libiberty.a, which is not installed by default on Mac OS-X and on certain Linux distributions. Most distros have that prepackaged in libbinutils or libbinutils-devel, or libbinutils2-devel (e.g. Mandriva Linux(2005 LE) has it in libbinutils2-devel-2.15.90.0.3-1mdk). Never download and install another version of libbinutils than the one designed for you distro or bad things may happen.
To enjoy the full capabilities of Lush, you may also want to install the Gnu Scientific Library (GSL or libGSL). Most Linux distros have it in two packages libgsl or libgsl0 and libgsl-devel or libgsl0-devel. LibGSL can also be installed from source, which can be obtained at http://sources.redhat.com/gsl
Another nice optional package to install is libSDL and its development libraries (e.g. on Mandriva Linux libSDL1.2-1.2.7-9mdk and libSDL1.2-devel-1.2.7-9mdk). It is also preferrable to set up OpenGL correctly on your machine (include the development packages when installing OpenGL).
1.1. Starting Lush |
amiga% lush LUSH Lisp Universal Shell (compiled on Oct 17 2004) Copyright (C) 2002 Leon Bottou, Yann Le Cun, AT&T Corp, NECI. Includes parts of TL3 Copyright (C) 1987-1999 Leon Bottou and Neuristique. Includes selected parts of SN3.2: Copyright (C) 1991-2001 AT&T Corp. This program is free software; it is distributed under the terms of the GNU Public Licence (GPL) with ABSOLUTELY NO WARRANTY. Type `(helptool)' for details. +[/home/yann/lush/sys/stdenv.dump] [lushrc.lsh] ?
To exit Lush, press Control-D or enter (exit) at the Lush prompt.
If you have emacs installed on your system, it is highly recommended to run Lush from within Emacs so that you may enjoy such niceties as command-line editing, history, and IDE-like features. See the next section "Configuration" for details on how to do this.
1.2. Configuration |
Many will prefer to run Lush under Emacs. To make this convenient, the following line should be added to the .emacs file in your home directory:
(load "your-lush-dir/etc/lush.el")This file provides two functionalities in Emacs:
Emacs's Lisp/Lush mode provides a set of nice command to facilitate Lisp programming. Among them are C-M-e (Ctrl-Meta-e) to jump to the end of a function, C-M-a to jump to the beginning, C-M-q to indent/format a function, and C-M-x to send the function or expression in which the cursor is to the interpreter (assuming it is running in the *inferior-lisp* buffer).
1.3. Reporting Bugs and Problems |
If you want to request a new feature, go to http://sourceforge.net/tracker/?atid=410166&group_id=34223&func=browse , and click "Submit New".
1.4. Getting Help and Documentation Browsing |
? ^Aregex Search Results for: regex 1. Regular Expressions (regex). 2. (regex-match <r> <s>) 3. (regex-extract <r> <s>) 4. (regex-seek <r> <s> [<start>]) 5. (regex-subst <r> <s> <str>) 6. (regex-rseek <r> <s> [<n> [<gr>]]) 7. (regex-split <r> <s> [<n> [<gr> [<neg>]]]) 8. (regex-skip <r> <s> [<n> [<gr> [<neg>]]]) 9. (regex-count <r> <s>) 10. (regex-tail <r> <s> [<n> [<gr> [<neg>]]]) 11. (regex-member <rl> <s>) 12. (glob <regex> <l>) choice? 2 ------------------------------------------------------------------------ (regex-match <r> <s>) [DX] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Returns <t> if regular expression <r> exactly matches the entire string <s>. Returns the empty list otherwise. Example: ? (regex_match "(+|-)?[0-9]+(\\.[0-9]*)?" "-56") = t = () ?
The list of symbols whose name contains a particular string can be listed using "^Ssymbolstring".
? ^Sregex cleanup-regex regex regex-count regex-member regex-rseek regex-skip regex-split regex-tail regex_extract regex_match regex_seek regex_subst = ()
1.5. Basic Syntax |
Here is an example of how an expression typed at the Lush prompt is evaluated and its result printed:
? (sqrt 4) = 2
The list notation is also used for what we generally think of as infix operators, which in Lush (and other Lisps) are just functions like any other. Instead of writing 3+4 , we write (+ 3 4) :
? (+ 3 4) = 7
Arguments of most functions are evaluated before the function is called, which allows nested expression:
? (sqrt (+ 3 4)) = 2.6458
Words like sqrt or + in the previous example are called symbols. Symbols can be assigned a value. The value of the symbol sqrt in the above example is a function that returns the square root of its argument. The value of a symbol can be assigned to any lisp object, and therefore is used as the basic mechanism for variables. Assigning a value to a symbol is generally performed with the function setq :
? (setq x 5) = 5 ? (* x x) = 25 ? (setq x "cou") = "cou" ? (setq x (concat x x)) = "coucou"
When not assigned to a value, symbols contain nil , which is printed as (and equivalent to) the empty list " () ". The empty list nil is used to represent the value "FALSE" returned by a boolean expression like
(> (- 2 4) 1) = ()
Variables can be set to any type of Lush object. In interpreted mode, the types of the variables need not be declared, but they must be declared if the code is to be compiled (more on that later). Variables can be assigned numbers, matrices and vectors, strings, lists, functions, macros, classes, hash table, and several other predefined objects types (such as windows and files), as well as user-defined objects (instances of user-defined classes).
1.5.0. Symbol Names and Special Characters |
By convention, sub-words of a symbol name are often delimited by hyphens and occasionally by dots:
gsl-my-new-pointer brace.read
1.6. Basic Types |
Some of the types can be entered as literals:
Numbers and strings have the interesting property that they evaluate to themselves, i.e. they return themselves when typed on the line or in an expression. On the other hand, symbols evaluate to their assigned value, and lists evaluate to the result of applying their first element (interpreted as a function) to the rest of the elements (the arguments). That's why the symbol and the list in the above example are preceded by a quote. The quote prevents what follows from being evaluated. Without the quote coucou would return its value (or the empty list () if it has no value), while (1 2 3) would produce an error because 1 is not a function.
1.7. Function Definitions |
(de <name> (<arg1>....<argn>) <body1>....<bodym>)
Here is a specific example:
? (de square (x) (* x x)) = square ? (square 4) = 16
This definition should be interpreted as follows. de means "define function", it is followed by the function name " square ", a list of formal arguments (x) , and the body of the function (* x x) , which means multiply x by itself and return the result. The return value of the function is the value returned by the last evaluation in the body bodym .
The definition of a particualr function can be displayed by typing ^Pfunctionname :
? ^Psquare (de square (x) (* x x) ) = t
The directive "^P", like "^A" introduced earlier is a so-called macro-character. Macro-characters are special combinations of characters that are translated into function calls by the interpreter. Lush has several predefined macro-characters to reduce typing for several commonly used interactive functionalities. A particularly useful one is "^Lfilename" which loads a Lisp source file (more on this later).
1.8. Loops, Conditionals, Local Variables, and Such |
? (setq z 0) = 0 ? (setq i 0) = 0 ? (while (< i 10) (incr i) (incr z (/ i))) = 2.929
the function incr increments its first argument by the value of its second argument, or by 1 if the second argument is not present, and returns the result. The function / called with one argument returns its inverse.
The above example is quite awful because it leaves two global variables i and z laying around for no reason. A cleaner way of doing things consists in using temporary variables that are automatically destroyed when they are no longer needed. This is done with the let* construct:
(let* ((z 0) (i 0)) (while (< i 10) (incr i) (incr z (/ i))))
In its most general form, the let* construct is used as follows:
(let* ((<var1> <value1>) ... (<varN> <valueN>)) <body1> ... <bodyP>)
It creates temporary local variable var1 ... varN , sets them to the values value1 ... valueN respectively, evaluates body1 ... bodyP and returns the result of the evaluation of bodyP . The values of the variables are destroyed upon exiting the let* . In fact, if a temporary variable created in a let* has the same name as a preexisting variable, the preexisting value will be temporary "masked" during the life of the temporary variable. The preexisting value will be restored upon exit:
(setq z 345) = 345 ? (let* ((z 0) (i 0)) (while (< i 10) (incr i) (incr z (/ i)))) = 2.929 ? z = 345
For the interested reader, Lush uses "dynamic variable binding", in contrast with many other languages (particularly compiled ones) that use lexical scoping.
The code above can be encapsulated in a function using the de construct:
? ;; compute the n-th term of the harmonic series ? (de harmonic (n) (let* ((z 0) (i 0)) (while (< i n) (incr i) (incr z (/ i))))) = harmonic ? (harmonic 10) = 2.929 ? (printf "%18.14f\n" (harmonic 10)) 2.92896825396825 = ()
In Lush, the text that follows one or several semi-colons on a line is taken as comments and ignored by the interpreter.
Conditional constructs include if , when , cond , and selectq . The basic form of the if construct is as follows:
(if <conditional-expression> <evaluate-if-non-nil> <evaluates-if-nil-1> <evaluates-if-nil-2> ..... <evaluates-if-nil-n>)
If the conditional expression returns a non-nil result, the expression evaluate-if-non-nil is evaluated and its result returned. If it returns nil, the remaining expressions are evaluated, and the result of the last one is returned. Multiple expressions can be grouped in the evaluate-if-non-nil part by grouping them in a progn construct: (progn expr1 ... exprN ) . To illustrate the use of if , here is a particularly stupid but didactic implementation of a function that compute the n-th Fibonacci number:
(de fibo (n) (if (= n 0) ; test if n equals 0 0 ; if yes, return 0 (if (= n 1) ; else test if n equals 1 1 ; if yes return 1 (+ (fibo (- n 1)) (fibo (- n 2))))))
This is an example of recursive function (a function that calls itself), a concept dear to the heart of many Lisp aficionados, but whose usage is somewhat discouraged in Lush (the Lush interpreter likes recursive functions, but the Lush compiler doesn't like them at all).
1.9. Function Compilation |
For starters, to avoid inefficient run-time type-checking in compiled code, one must define the type of all the variables before a function can be compiled. The compilable version of harmonic is:
? ;; define the function ? (de harmonic (n) ((-double-) n) (let* ((z 0) (i 0)) ((-double-) z i) (while (< i n) (incr i) (incr z (/ i))))) = harmonic
The lines ((-double-) n) and ((-double-) z i) declare the type of the argument n and the local variables z , and i as double precision floating point numbers. Before we compile the function, let's measure the number of CPU seconds required to compute the sum of the million-th harmonic sum in interpreted mode (this is on a 800MHz Pentium running Linux):
? (time (harmonic 1000000)) = 2.05
We can compile the function by simply typing (dhc-make "junk" harmonic) . Lush will translate the Lisp code into C and write the C source into the file "C/junk.c". The C compiler will then compile the code and generate the object file "C/ architecture /file.o", where architecture is the name of the architecture on which Lush is currently running (e.g. i686-pc-linux-gnu). This allows to maintain object files for different architectures in a single source directory tree. Then, Lush's dynamic loader will automagically load the object code back into the interpreter.
(dhc-make "junk" harmonic) Preprocessing and parsing harmonic ... Generating C for harmonic ... gcc -DHAVE_CONFIG_H -I/home/yann/lush/include -DNO_DEBUG -Wall -O3 -funroll-loops -mcpu=i686 -pthread -c /tmp/C/junk.c -o /tmp/C/i686-pc-linux-gnu/junk.o = "/tmp/C/i686-pc-linux-gnu/junk.o"Now let's see how many CPU seconds it takes to execute the compiled version of the code. We need to repeat the code a few times to get a meaningful figure:
? (time (repeat 50 (harmonic 1000000))) = 2.37The speedup over the interpreted version is around 45. An important point to remember is that the interpreter always treats numbers as double precision floating point, while numbers in compiled code are treated as specified by their type declaration. Lush understands the following scalar types: -ubyte-, -byte-, -short-, -int-, -float-, -double-, -bool-, -gptr- . they correspond respectively to the C types unsigned char, signed char, short, int, float, double, unsigned char, void* .
Just for curiosity, let's look at the C code generated by Lush's Lisp to C compiler in current-dir/C/junk.c :
/* * FUNCTION harmonic */ extern_c real C_harmonic (real L1_n) { TRACE_PUSH ("C_harmonic"); { real L_Tmp0; { real L2_1_z; real L2_2_i; L2_1_z = 0; L2_2_i = 0; L_Tmp0 = 0; /* While loop */ goto L_1; L_0: { L2_2_i = (L2_2_i + 1); L2_1_z = (L2_1_z + (1 / (real) L2_2_i)); L_Tmp0 = L2_1_z; } /* While loop test*/ L_1: { if ((L2_2_i < L1_n)) goto L_0; } } TRACE_POP ("C_harmonic"); return L_Tmp0; } }
It looks a bit funny with all these gotos , but it's reasonably simple and straightforward C code.
1.10. Program Files |
;; a function that computes the sum of the inverses of the first n integers. (de harmonic (n) ((-double-) n) (let* ((z 0) (i 0)) ((-double-) z i) (while (< i n) (incr i) (incr z (/ i))))) ;; compile the function to toto.c and toto.o ;; if the first arg is nil, the .c file has the same ;; base name as the file being loaded. (dhc-make () harmonic)
When loading this file, Lush processes its content as if it were typed at the prompt. Therefore, loading this file will define harmonic, and then compile it. If you subsequently reload the same file, the compilation will only occur if the source file toto.lsh was modified since the last compilatione.
The use of GNUemacs to edit Lush files is very strongly recommended as it has a special Lisp mode that flashes matching parentheses, highlights keywords and comments, and automatically indents Lisp expressions (using ALT-CTRL-Q). More details on how to setup Emacs are given in the "setting things up" section of this tutorial.
1.11. On-Line Documentation |
#? (harmonic <n>) ;; computes the sum of the inverses of the first <n> integers ? (de harmonic (n) ((-double-) n) (let* ((z 0) (i 0)) ((-double-) z i) (while (< i n) (incr i) (incr z (/ i))))) ;; compile the function to toto.c and toto.o (dhc-make () harmonic)
The string after the "#?" is what will be searched when search feature of (helptool) is invoked on this file. Contiguous comment lines immediately following the "#?" line constitute the text of the help. After this file is loaded, the on-line help for harmonic and all the functions in the same file can be displayed with:
(helptool "toto.lsh")
1.12. Multiple File Programs and Search Path |
#? *** My Cool Signal Processing Application ;; this is my really cool dsp application. (libload "/home/yann/lsh/dsp") .....(the .lsh extension is automatically added if required).
The function libload has an interesting features: it remembers which files were already libloaded and loads them only once into the interpreter (subsequent calls to libload with a previously libloaded file will have no effect). There is an exception to this behavior: libload keeps track of the tree of dependencies between Lush program files, and also keeps track of the times of last modification of each file. When a file is loaded with libload , all the files on which this file depends are also loaded, but only if they have been modified since last time they were libloaded. Here is a possible scenario: file A libloads files B and C. File A is libloaded, as a result file B and C are libloaded. File B is modified. File A is libloaded again, as a result B is re-libloaded, and A is re-libloaded. C is not re-libloaded since it was previously libloaded and was not modified. This behavior is recursive (it applies to all files directly or inderectly libloaded by A).
In certain cases, the lower-level function load may be preferable. load simply loads a file:
? (load "/home/yann/lsh/dsp")
Putting absolute path names in a program is generally a bad idea since programs have a pernicious tendency to move from time to time. The solution is to rely on Lush's search path mechanism. When any loading directive is executed, Lush looks for the file in the current directory, and then in the search path. The default search path includes lush/local , lush/packages , lush/lsh , lush/sys and several subdirectories of lush/lsh . The search path can be user-extended with the addpath function:
(addpath "/home/yann/lsh")
now, loading "dsp.lsh" only requires:
(libload "dsp")
1.13. C Function Calls from Lush |
Let's say you have written a C file called titi.c with the following content:
float sq(float x) { return x*x; }
You have compiled the file and produced the object file titi.o . Calling sq from the Lush interpreter is as simple as the following. First, dynamically load the object file into Lush
? (mod-load "titi.o")then, write a lisp function whose only purpose is to call sq .
? (de square (x) ((-float-) x) (cpheader "extern float sq(float);") (to-float #{ sq( $x ) #} ))
Here is what the above means: (de square (x) ...) means define a new Lisp function called square with a single argument x . ((-float-) x) simply declares x as float. (to-float ....) converts its argument to float (like a cast in C). The sequence #{ .... #} allows to insert C code within Lisp code. Therefore #{ sq( $x ) #} simply calls the C function sq and returns the result. Lisp variables can be inserted in in-line C code by prepending a Dollar sign, thus $x refers to the Lisp float variable x . The cpheader directive allows one to include a string in the "header" section of the C files generated by the Lisp to C compiler. We use it here to specify the type of the argument and the return value of sq .
We can now compile the above function using:
? (dhc-make () square)Two file C/square.c and C/i686-pc-linux-gnu/square.o will be generated with the C source and the object code for square . The object code will be automatically loaded into Lush. Now square can be called from the Lisp interpreter:
? (square 5) = 25
In the above example, the to-float casting function was used to tell the compiler what type the returned value has. The following "cast" functions are available: to-float , to-int , to-double , to-bool , to-gptr . These functions cast their argument to the corresponding type.
1.14. Mixing Lisp and In-Line C Code |
(de harmonic (n) ((-double-) n) (let ((r 0)) ((-double-) r) #{ { double i=0; while (i<$n){ i+=1.0; $r+=1/i; }; } #} r)) (dhc-make () harmonic) Preprocessing and parsing harmonic ... Generating C for harmonic ... gcc -DHAVE_CONFIG_H -I/home/yann/lush/include -DNO_DEBUG -Wall -O3 -funroll-loops -mcpu=i686 -pthread -c /tmp/C/junk.c -o /tmp/C/i686-pc-linux-gnu/junk.o = "/tmp/C/i686-pc-linux-gnu/junk.o" ? (time (repeat 50 (harmonic 1000000))) = 2.36
That's about the same time as the version written in compiled Lisp. In this case, writing directly in inline C was not particularly advantageous, but there are cases where writing in Lisp is inefficient, awkward, or even outright impractical. In these cases one can resort to inline C implementations.
1.15. Lists |
? (+ 1 2) = 3
To create a list, we must prevent the evaluation from happening. This is done with the quote function or the "'" (quote) macro-character:
(quote (+ 1 2)) = (+ 1 2) ? '(+ 1 2) = (+ 1 2)
Lists can be put into variables like any other Lisp object:
(setq x '(+ 1 2))
List elements can be any lisp object, including other lists. Here is a list with a number, a string, a list of numbers, a symbol, and a list, and a number:
(setq x '(1 "coucou" (4 5 6) asd (7 (8 9) 10) 20))
Here is another example where the same list as above is assembled from its members using the list function:
(setq x (list 1 "coucou" '(4 5 6) 'asd '(7 (8 9) 10) 20))
The first element (the head) of a list can be obtained with the car function, and the rest of the list (tail) with the cdr function (pronounced kudder). The cons function can be used to put a head and a tail together:
? (setq x '(+ 1 2)) = (+ 1 2) ? (car x) = + ? (cdr x) = (1 2) ? (setq z (cons '+ '(1 2))) = (+ 1 2)
A list can be evaluated:
(setq x '(+ 1 2)) = (+ 1 2) ? (eval x) = 3
Operations can be performed on all the elements of a list using the all construct:
? (setq z '(1 3 5 7 9)) = (1 3 5 7 9) ? (all ((x z)) (- x 1)) = (0 2 4 6 8)
This says, make a temporary variable x , and avaluate the body of the all with x set to each element of z in sequence. Then return a list of all the results.
This section only scratched the surface of what can be done with lists. The reader is referred to the List section in the "Core Interpreter" chapter of the Lush manual.
1.16. Strings and Regular Expressions |
1.17. Scalars, Vectors, Matrices, Tensors, and Storages |
? (setq m (matrix 10 8 4)) ; create 3D matrix = ::INDEX3:<10x8x4> ? (m 3 4 2 45.6) ; set value of element (3,4,2) to 45.6 = ::INDEX3:<10x8x4> ? (m 3 4 2) ; get value of element (3,4,2). = 45.6
Tensors of various basic types can be created with the functions listed below. Each function has two versions, the regular version initializes all the elements to zero, while the version with -nc at the end do not (no clear) and are a bit faster. All of these functions take 0 to 8 integer arguments that are the sizes in each dimension:
Matrices of doubles can be entered literally using square brackets:
(setq m [[0 2 3 4][5 6 7 8]])Matrices of other types can be specified by adjoining an apropriate character after the open bracket. Here is how to create a vector of floats:
(setq m [f 0 2 3 4])Characters f, i, s, b, u, a specify float, int, short, byte, ubyte, and atom respectively. Here is an example with atoms:
(setq m [a "choucroute" "garnie"])
Matrices are really composed of two separate entities:
1.17.0. Working with Storages |
When directly accessing storage elements, storages behave like 1D idx (vectors). Storages can be created, allocated in memory, or memory-mapped to a file. Storage creation function are provided for the following types: doubles, floats, ints, shorts, bytes, unsigned bytes, generic pointers, and lisp object pointers.
1.17.1. Manipulating Tensors and IDX |
1.17.2. Tensor Iterators |
(idx-bloop ((s1 idx1) [ (s2 idx2) [...(sn idxn) ]]) body)
Each si will be an idx with one less dimension than the corresponding idxi , and will simulataneously loop over the successive "slices" of idxi for each possible value of the first index. In other words, applying function myfunc to each element of a vector v1 and putting the result in the corresponding element in v2 can be done with:
(idx-bloop ((x1 v1) (x2 v2)) (x2 (myfunc (x1))))
x1 and x2 are scalars (i.e. zero-dimensional tensors). The above function work just as well is v1 and v2 are n -dimensional tensors and myfunc accepts n-1 -th dimensional tensors as arguments. It should not be assumed that the multiple evaluations of the body are executed in sequence. They may be executed in parallel on some implementations of Lush. A similar function idx-eloop iterates on the last dimension of a tensor instead of the first dimension. For example, the matrix product operation C = A*B can be written as follows:
(de idx-m2timesm2 (A B C) (idx-eloop ((Bj B)(Cj C)) (idx-m2dotm1 A Bj Cj)))where idx-m2dotm1 is the usual matrix-vector product. The idx-eloop construct simultaneously iterates over all columns of B and C (naturally, the function idx-m2timesm2 is predefined).
Another idx iterator allows to easily write the inner loop of a tensor operation in C, while leaving all the bookkeeping to the Lisp:
(cidx-bloop (i_1 [i_2...i_n] (c_1 l_1) [(c_1 l_1)...(c_m l_m)) p_1 [p_2...])
This is somewhat equivalent to n nested idx-bloop s, which will simultaneously loop over the first n dimensions of idxs l_1 to l_m . The arguments i_1 to i_n are strings containing names of C local variables that will be set to the loop index in each of the n dimensions. At each iteration, the C variables provided in strings c_1 to c_m will point to the appropriate values in the idxs l_1 to l_m . For example, the following function will fill matrix a with cos(i+j).
(de foo (a) ((-idx2- (-flt-)) a) (cidx-bloop ("i" "j" ("a" a)) #{ *a = cos(i+j); #}) a)
The return value is (like in idx-bloop) the last idx specified in the declaration (in the example above, the return value is superfluous).
1.17.3. IDX Manipulations |
(narrow m n s [o]) returns a clone of idx m where the size in the n -th dimension is reduced to s elements, offset by o . This can be used to select bands or rectangular areas in matrices.
? (setq m [[0 2 3 4][5 6 7 8]]) = [[ 0.00 2.00 3.00 4.00 ] [ 5.00 6.00 7.00 8.00 ]] ? (narrow m 1 2 1) = [[ 2.00 3.00 ] [ 6.00 7.00 ]]
(select m n s) return a clone of m , with the n -th dimension removed, and which is the s -th "slice" of m , in the n-th dimension. For example, the 3rd column of matrix m in the above example (which is a vector) can be obtained with
? (select m 1 2) = [ 3.00 7.00 ]
(unfold m n ksize step) returns an idx with an added dimension at the end of m obtained by "unfolding" n -th dimension. More explicitely, successive rows in the last dimension will contain successive, possibly overlapping, subsets of size ksize of consecutive elements in the n -th dimension. The separation between successive subsets is step . Here is an example:
? (setq m [0 1 2 3 4 5 6 7]) = [ 0.00 1.00 2.00 3.00 4.00 5.00 6.00 7.00 ] ? (unfold m 0 3 1) = [[ 0.00 1.00 2.00 ] [ 1.00 2.00 3.00 ] [ 2.00 3.00 4.00 ] [ 3.00 4.00 5.00 ] [ 4.00 5.00 6.00 ] [ 5.00 6.00 7.00 ]]
No data is copied or replicated in the process, i.e., the three occurences of the number 2 in the above example actually come from a single memory location. The size of the new dimension is ksize . If dimn is the size of the n -th dimension of m , the size of the returned idx in the n -th dimension is (dimn-ksize)/step + 1 . The values of dimn , ksize , and step must be such that the new size is an integer.
The unfold function essentially allows to reduce a discrete convolution to a matrix-vector product. Here is an example:
? (setq m [1 1 0 2 3 4 2 0]) = [ 1.00 1.00 0.00 2.00 3.00 4.00 2.00 0.00 ] ? (setq kernel [-1 2 -1]) = [-1.00 2.00 -1.00 ] ? (setq output (matrix 6)) = [ 0.00 0.00 0.00 0.00 0.00 0.00 ] ? (idx-m2dotm1 (unfold m 0 3 1) kernel output) = [ 1.00 -3.00 1.00 0.00 3.00 0.00 ]
A subsampled convolution can be implemented by unfolding with a step larger than 1:
? (setq m [0 1 2 3 4 5 6 7 8]) = [ 0.00 1.00 2.00 3.00 4.00 5.00 6.00 7.00 8.00 ] ? (setq z (unfold m 0 3 2)) = [[ 0.00 1.00 2.00 ] [ 2.00 3.00 4.00 ] [ 4.00 5.00 6.00 ] [ 6.00 7.00 8.00 ]] ? (setq kernel [1 2 1]) = [ 1.00 2.00 1.00 ] ? (idx-m2dotm1 z kernel output) = [ 4.00 12.00 20.00 28.00 ]
Naturally, there is no real need for most programmers to use the unfold construct because the standard lush library contains efficient 1D and 2D convolutions .
(idx-transclone m dimlist) returns a clone of idx m where the dimensions have been permuted according to the list of dimension indices dimlist . For example (idx-transclone m '(0 2 1)) permutes the second and third dimensions (again, no data is moved or copied by this operations).
A few functions are provided to physically hack an idx to change a dimension, a modulo, or the offset (repectiveley (idx-changedim m n v), (idx-changemod m n v), (idx-changeoffset m n) ).
1.17.4. Simple Tensor Operations |
(idx-XXX m [r])
where XXX is one of the above function, m is the input matrix (from 0 to 8 dimensions) and r is an optional result matrix which must have the same dimensions as m . If r is not present, the result is created and returned.
Also available are component-wise dyadic operations such as add, sub, mul, div . They take the form
(idx-XXX m1 m2 [r])
Next come dyadic operations where the second argument is a scalar, such as (idx-addm0 m s [r]) which adds scalar s to each element of tensor m , or (idx-addm0acc m s r) which does the same thing but accumulates the result in r , or (idx-dotm0 m1 s [r]) which multiplies each element of m by scalar s .
1.17.5. Operations between IDX and Scalars |
(idx-dotm0 m s [r])>where m is an idx of any type and any number of dimensions, s is a scalar (i.e. an idx with zero dimension), and r is an optional idx in which the result will be written. Turning a number into a scalar can be performed with ((double-matrix) number ) . So for example, multiplying all the elements of a matrix by 10 is done as follows:
? (idx-dotm0 [1 2 3] ((double-matrix) 10)) = [10.00 20.00 30.00 ]Similarly, function idx-addm0 adds a scalar to all the elements of an idx.
1.17.6. Contracting Operations with Scalar Results |
(idx-dot m1 m2 [r])>
Other such functions include idx-inf (smallest element), idx-sqrdist (sum of square difference between two tensor), idx-sum (sum of all terms), idx-sumsqr (sum of squares), idx-sup (largest element)
1.17.7. Special Inner and Outer Products |
1.17.8. Simple Linear Algebra |
1.18. Complex Numbers |
(libload "libnum/libcomplex")Creating a complex number is done just like creating a vector of 2 doubles, or using the macro new-complex , which is equivalent to (double-matrix 2) :
(setq z [3 4]) ; complex literal [real-part imaginary-part] (setq z (new-complex)) ; allocation of complex [0 0]Complex vectors and matrices can be created with complex-matrix :
(setq z (complex-matrix 3 3))which is exactly equivalent to:
(setq z (double-matrix 3 3 2))Several complex arithmetics and functions are provided. The function names all begin with the letter c . Complex dyadic functions include c+, c-, c*, c/, c**, clogb (addition, subtraction, multiplication, division, power, complex log with complex base). All commonunary operations on complex are also provided, including conjugate, inverse, square root, exponential, log, and trigonometric functions. More details can be obtained in the packages/libnum section of the Lush manual ( "libnum: Numerical Library" ).
The memory layout of complex vectors and matrices so defined are compatible with the LAPACK and GSL functions that take double complex vectors and matrices as arguments.
1.19. Object-Oriented Lush Programming |
Naturally, Lush has a number of built-in immutable classes such as numbers, list cells, strings, symbols, etc, to which users can add methods (but not slots). This section will concentrate on user-defined classes.
1.19.0. A New Class Definition |
(defclass rectangle object width leng)This can be interpreted as: define the class rectangle as a subclass of object with two slots width and leng . Subclasses inherit the slots of their superclass. (which is a moot point here since object has no slot).
A new instance of the class rectangle can be created with the function new :
(setq r (new rectangle))By default, the slots are left unfilled (set to the empty list). Accessing slots of an object can be done with the macro-character " : "
? (setq :r:width 10) = 10 ? (setq :r:leng 3) = 3 ? ^Pr ;;*** ::rectangle:819fa08, INSTANCE OF ::class:rectangle ;; FROM CLASS: rectangle ;; width=10 ;; leng=3 ;; FROM CLASS: object
Classes can be given methods (or virtual member functions in the C++ semantics) defined as in the following example:
(defmethod rectangle area () (* leng width))This defines the method area for the class rectangle which takes no argument, and returns the product of the slots leng and width . This is essentially similar to defining a function with de , except that within the body of a method, the slots of the object are directly accessible by name as if they were local variables. Calling the above method can be done as follows:
? (==> r area) = 30
which can be read as "send to r the message area ".
Methods can be passed arguments, and can refer to the current object through the special variable this . Here is an example of how to compute the volume of a box whose base is the current object and height is passed as argument:
? (defmethod rectangle volume (height) (* height (==> this area))) = volume ? (==> r volume 10) = 300
1.19.1. Inheritance |
? (defclass box rectangle height) = boxSetting the slots of an object directly as we did in the rectangle example is considered bad practice. A better alternative is to fill the slots when the object is created. This can be done by defining a constructor method. The constructor is a method that has the same name as the class. It is called whenever the new function is called to create an object of that class. Here is a constructor for box :
? (defmethod box box (l w h) (setq leng l) (setq width w) (setq height h)) = box ? (setq monolith (new box 9 4 1)) = ::box:8138958 ? ^Pmonolith ;;*** ::box:8138958, INSTANCE OF ::class:box ;; FROM CLASS: box ;; height=1 ;; FROM CLASS: rectangle ;; width=4 ;; leng=9 ;; FROM CLASS: object = () ? (defmethod box volume () (* width leng height)) = volume ? (==> monolith volume) = 36
An alternative way to define the volume method would have been to multiply the height by whatever result the area method inherited from rectangle returns:
(defmethod box volume () (* height (==> this area)))Class hierarchies are nice, but beginner should be careful not to create deep hierarchy trees with more than 3 or 4 levels of inheritance. Experience proves that deeper trees result in hard-to-maintain code.
Unlike C++, Lush neither support multiple inheritance, nor private slots and methods.
1.20. Input and Output |
1.20.0. Simple String Input/Output |
Normal output can be redirected to a file with the writing construct. For example, the code below will write 10 random integers to the file "blah":
(writing "blah" (for (i 0 9) (printf "%d\n" (int (rand 0 100)))))
Similarly, input can be redirected from a file with the reading construct. For example, the code below will read the first line from text file "blah":
(reading "blah" (read-string))
The first argument of reading and writing can be one of the following:
(writing "| sort -n" (for (i 0 9) (printf "%d\n" (int (rand 0 100)))))
1.20.0.0. Filename Processing |
The existence of a plain file "/asd/qwe.xxx" can be determined with (filep "/asd/qwe.xxx") , and the size of the file can be obtained with (file-size "/asd/qwe.xxx") (which returns nil if the file does not exist).
More sophisticated ways to examine the properties of a file are provided through the function fileinfo .
1.20.1. Complete File Reading |
1.20.2. Lush Object Reading/Writing |
1.20.2.0. Text File Reading |
Conversely, printing Lush objects to a text file or on the terminal can be done with (print some-lush-object) .
1.20.2.1. Binary Serialization |
Saving a Lush object (say contained in variable some-lush-object ) to file "myfile.bin" can be done with:
(writing "myfile.bin" (bwrite some-lush-object))Restoring the object can be done with:
(reading "myfile.bin" (setq some-lush-object (bread)))Functions bread and bwrite stand for "binary read" and "binary write".
1.20.3. C-like stdio Functions |
1.20.4. Socket Communication |
There is also a simple way to start another Lush process (on the same machine as the current process or on another one), and to send expressions to that remote process to be evaluated. This is implemented through the RemoteLush class. An instance of Lush must be running in "slave" mode on the remote machine; this is done by starting the script "lushslave".
See the documentation for "Remote Lush Execution" for more details.
1.21. Graphics |
Opening a graphic window on the screen is simply done with:
(new-window [<x> <y> [<w> <h> [<name>]]])where the arguments are respectively the position, size and name of the new window.
Drawing in a window can be done with a collection of simple primitives:
(font "Helvetica-Bold-20") (gprintf 100 100 "salut les potes")
Double-buffered and flicker-free redrawing can be done with the graphics-batch function. Used in the form (graphics-batch exp1 exp2 ... exp3) , it will evaluate all its argument, but any graphic operation performed in that time will be performed into an off-screen graphic buffer. This graphic buffer will be slapped on the window as the call to graphics-batch terminates. This allows smooth double-buffered animation:
(while not-finished (compute-stuff) (graphics-batch (draw-stuff-1) (draw-stuff-2) (draw-stuff-3)))
Here is an example:
(libload "libimage/image-io") (setq m (image-read-rgb (concat-fname lushdir "packages/video4linux/demos/jabba-320x240.jpg"))) (new-window) (for (i 0 99) (graphics-batch (cls) (rgb-draw-matrix i i m)))
1.21.0. Multiple Window Management |
(setq win1 (x11-window 0 0 200 200 "Window 1")) (setq win2 (x11-window 0 220 200 200 "Window 2"))Graphic commands occur in whatever window object is bound to the window symbol. So drawing a line into win2 can be done with:
(let ((window win2)) (draw-line 10 10 100 100))Closing a window is simply done by setting its handle to nil:
(setq win1 ())
1.21.1. Editable Graphics via comdraw/ivtools Driver |
Opening a comdraw window is done with:
(setq window (comdraw-window x y width height))Subsequent graphic commands will occur in the comdraw window. The function graphics-batch takes a new meaning in a comdraw window: it is used to group together the set of graphic objects created by the graphic commands called within the scope of the graphic-batch .
1.21.2. PostScript File Graphics |
(setq window (ps-window width height filename))filename is the name of the PostScript file that will be generated.
1.22. Curve Plotting |
(setq p (new plotter x y width height))The plotter will attach itself to the current window, place its upper left hand corner at location x,y , and set its dimension in the window to width,height . A window will be automatically open if there is no current window. Using a plotter consists in adding "curves" to it and then calling the method "redisplay" to actually render it in the window to which it is attached.
Here is how to plot the log function in blue on the 0, 10 interval:
(==> p PlotFunc "log" log 0 10 0.1 (alloccolor 0 0 1)) (==> p redisplay)Here is how to plot a segment of ellipse in red:
(==> p PlotXY "lips" sin cos 0 5 0.1 (alloccolor 1 0 0)) (==> p redisplay)Here is how to plot a sparsely sampled green sinusoid (and indicating each sample by a filled circle):
(setq x (range 0 10 0.5)) (setq y (all ((v x)) (sin v))) (==> p plotlists "sine" x y (alloccolor 0 1 0) closed-circle) (==> p Redisplay)A plotter can be moved or resize using the methods move and setsize :
(setq p move 20 20) (setq p setsize 100 100) (==> p Redisplay)
1.23. Lush Executable Shell Scripts |
Rather than a long explanation, here is an example of a Lush script file:
#!/bin/sh exec lush "$0" "$@" !# (printf "Capitalizing the arguments:\n") (each ((arg (cdr argv))) (printf "%s %s\n" arg (upcase arg)))This works because Lush recognizes everything between #! and !# as a multi-line comment. This assumes that your lush executable is in your shell command search path.
In that mode, Lush does not display the usual startup banner. It load the standard environment ( sys/stdenv.dump or sys/stdenv.lsh ). It sets the variable argv to a list of strings, where the strings are the command line arguments (including the command itself). Finally it reads and evaluates the content of the script file. The script terminated when the evaluation of the last expression in the script returns or when the function exit is called.
The library libc/shell.lsh contains several functions to facilitate the writing of shell scripts. Among other things, it contains Lisp versions of common shell functions such as ls , cp , mv , etc.
Here is another script example: a photo slideshow program in 12 lines of Lush. Put this in a file (say lush-slideshow ), make it executable with chmod a+x lush-slideshow . Then, at the shell prompt, type: lush-slideshow photo1.jpg photo2.jpg photo3.jpg :
#!/bin/sh exec lush "$0" "$@" !# (libload "libimage/image-io") (libload "libimage/rgbaimage") (setq window (x11-window 1 1 640 480 "Slide Show")) (while t (each ((f (cdr argv))) (rgb-draw-matrix 0 0 (rgbaim-resize (image-read-rgba f) (xsize) (ysize) 0)) (sleep 4)))
1.23.0. Executable GUI Applications |
#!/bin/sh exec $0 !# ;; A simple Ogre GUI demo (ogre) (wait (new autowindowobject 10 10 100 100 "Simple Lush GUI Demo" (new column (new stdbutton " hit me " (lambda (c) (printf "OUCH\n"))) (new stdbutton " feed me " (lambda (c) (printf "CRUNCH\n"))))))The wait function causes Lush to sit around and keep processing events until the argument of wait becomes nil (presumably as the result of processing an event). If we did not call wait in the above example, the script would terminate immediately after opening the Ogre window. The argument to wait is the WindowObject in which the Ogre application is set up. It is destroyed as soon as the user closes the window, hence terminating the script.
1.24. Lush Standalone Binary Executables |
At this time, this is only possible for Lush functions that are fully compiled (no interpreted part is allowed).
This functionality is performed by the make-standalone function. the make-standalone function can generate a complete C source directory with all the supporting functions and macros to make a particular Lush function run.
The generated code can be redistributed with no restriction (it is not subject to the obligations of the GPL).
See the make-standalone documentation for more details.
1.25. Ogre GUI Toolkit |
(ogre) (de coucou (c) (printf "coucou\n")) (new autowindowobject 10 10 () () "ouch" (new stdbutton "click me" coucou))
The autowindowobject is an ogre object that opens a new window and adjutst itself to fit the size of its content. In this case, the content is a single standard button (a.k.a. stdbutton ). The stdbutton constructor takes a string and a function with one argument which is called each time the button is pressed. Now, here is a slightly more sophisticated example:
(ogre) (libload "libimage/image-io") (libload "libimage/rgbaimage") ;; load image (defvar image (image-read-rgba (concat-fname lushdir "packages/video4linux/demos/jabba-320x240.jpg"))) ;; make an imageviewer object (setq image-viewer (new imageviewer 240 220 image t)) ;; define a few functions to tell the imageviewer to display rotated images (de rot90-right (c) (==> image-viewer settext (rgbaim-rot90-right image))) (de rot00 (c) (==> image-viewer settext image)) (de rot90-left (c) (==> image-viewer settext (rgbaim-rot90-left image))) ;; create the GUI window (new autowindowobject 10 10 () () "ouaf" ;; insert a column (new column ;; make a row of buttons (setq button-row (new row (new stdbutton "Left" rot90-left) ; rotate left when clicked (new stdbutton "Straight" rot00) ; straighten up when clicked (new stdbutton "Right" rot90-right))) ; rotate right when clicked ;; insert imageviewer image-viewer))
1.26. Simple Directmedia Layer(SDL) Interface Usage |
Lush provides a high-level interface to some of those functions in sdl/libsdl . It includes higher-level classes, such as screens and sprites, with pixel-accurate collision detection. This allows rapid development of graphic-intensive interactive applications such as real-time 2D games.
A tutorial on writing simple games with SDL is available in the Tutorial section of the Lush manual. A full documentation of the SDL interface is available at "SDL: Simple DirectMedia Layer"
The present section is a quick introduction to the libsdl library. Before you start, make sure SDL and all its development libraries and header files are installed on your system. If you are on Linux, those things are available as packages with your distro.
Loading and initializing the libsdl library is performed with:
(libload "sdl/libsdl") (sdl-initialize)Next, we can open an SDL screen. libsdl provides three kinds of SDL screen classes.
1.26.0. sdlidx-screen class: Mapping the Contents of an IDX to an SDL Screen |
(setq depth 3) (setq m (ubyte-matrix 480 640 depth)) (setq screen (new sdlidx-screen m "my title"))If the depth parameter is 3, each element of matrix m is a component of an RGB pixel. So for example (m y x 0) is the R component of pixel (x, y) (from the top left of the screen). If the depth parameter is 4, each element of m is a component of an RGBA pixel. The A component is the Alpha channel (transparency). An alpha value of 255 is opaque, while 128 is half transparent. The matrix m can be seen as the "back buffer" of a double-buffered screen. Changing values in m will not immediately change the corresponding pixel on the screen. Once the desired RGBA image has been written in m , slapping it on the screen can be done with (==> screen flip) . If the screen was opened with depth =4, the image displayed will be an alpha-blended mixture of the previously displayed image and the content of m . Here is a complete example:
(libload "sdl/libsdl") (libload "libimage/image-io") (libload "libimage/rgbaimage") (sdl-initialize) (setq m (ubyte-matrix 480 640 3)) (setq screen (new sdlidx-screen m "my picture")) (setq photo (rgbaim-resize (image-read-rgba "mypicture.jpg") 640 480 0)) (idx-copy (narrow photo 2 3 0) m) (==> screen flip)Subsequent calls to (==> screen flip) will refresh the screen with the content of m . Note that SDL screens (unlike regular Lush graphic windows) are not automatically refreshed when uncovered from behind other windows. That is why SDL screens should be used only for animated graphics and games. Refresh rates on SDL screens are likely to be faster than with regular Lush graphic windows.
1.26.1. sdl-screen class: Doubled Buffered Animations |
Opening an sdl-screen is simply done with (setq screen (new sdl-screen 800 600 "the title")) . Drawing into an sdl-screen is made very simple by the sdl-sprite class (as explained below), but low-level SDL drawing can be performed by creating SDL_surface objects and calling SDL_BlitSurface on the p slot of the sdl-screen .
1.26.2. sdl-sprite: Movable Animated Objects |
(setq sprite (new sdl-sprite screen 1)) (==> sprite load-frame "mysprite.png" 0 0 0)This creates a new sdl-sprite with id number 1 on screen screen , and loads frame 0 of this sprite with the image in file "mysprite.png" (the image can be in any format accepted by the SDL-image library dstributed with SDL). The three numerical arguments (all zeros in the example) are the frame number to be loaded, and the coordinates of the "handle" or "hot-point" of the sprite relative to the top-left corner.
Sprites can be moved around and drawn on the screen with:
(==> sprite move x y) (==> sprite draw) (==> screen flip)All drawing operations occur in the back buffer (not on the visible screen) until the call to (==> screen flip) .
The main loop of a typical application will look something like this:
(while (not stop) [get user input, update coordinates, and such] (==> screen clear) (==> sprite1 move x1 y1) (==> sprite2 move x2 y2) (==> sprite1 draw) (==> sprite2 draw) (==> screen flip))Sprites can have multiple frames. Switching between frames provides a mechanism for animating sprites. More details on how to write such applications (including reading keyboard events, animation and such) are given in the tutorial "Tutorial: Writing Games with Lush and SDL." . The code for that tutorial is available at .
1.26.3. sdlgl-screen class: OpenGL 3D Graphics in SDL Screens |
(setq screen (new sdlgl-screen 640 480 "my title"))An example of use of sdlgl-screen is available at .
1.27. OpenGL Usage |