Lush/Beginning Lush
This chapter covers the basic syntax of lush. For discussion of classes, see Classes and Objects.
Getting Started
editLike most interpreted languages, lush commands can be run interactively from the lush prompt, or as a batch of statements from a text file.
Hello World
editTo begin, start lush by typing "lush" or "lush.exe" (for windows) in a terminal:
mkg@FRIDGE:~$ lush
This will print a copyright notice, then put you in interactive mode with a lush prompt:
LUSH Lisp Universal Shell (compiled on Jul 13 2006)
Copyright (C) 2002 Leon Bottou, Yann LeCun, AT&T, 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 distributed under the terms
of the GNU Public Licence (GPL) with ABSOLUTELY NO WARRANTY.
Type `(helptool)' for details.
+[/usr/local/share/lush/sys/stdenv.dump]
[lushrc.lsh]
?
The following line will print the familiar first words:
$ (print "Hello world")
Result:
"Hello world"
= "Hello world"
Like its parent language lisp, lush calls commands using a space-separated list of items within a pair of parentheses:
(''function_name'' ''argument_1'' ... ''argument_n'')
Also notice that entering (print "Hello world")
not only printed "Hello world", but also returned a "Hello world" string. You can see this in the output above, where the terminal shows the result of the entered expression as being = "Hello world"
. This brings us to an important point: All lush statements have a return value. The closest equivalent to "void functions" in C/C++/Java are functions that return the empty list ()
. Lush has a C-like printf
function which does just that:
$ (printf "I am %d today\n" 9)
Result:
I am 9 today
= ()
Interactive mode features
editThere are some usability features built into the lush shell:
Online help
editThe following command will bring up a GUI interface to a reference dictionary of all things lush.
$ (helptool)
That is the first thing to try when looking for a particular function that you know must exist. Alternately, if you already know the function name, you can enter the following on the lush prompt:
$ ^A''function_name''
Tab completion
editIf you can't remember the exact name of the function or variable, you can type the first few letters of its name and hit tab. If there is an unambiguous completion, lush will fill in the rest of the letters for you. If there are more than one symbols that fit the bill, hitting tab again will display a list of possible completions. For example, typing (prin
and hitting tab twice will display the following:
$ (prin
prin printf print-window
print printrequester
Command history
editHitting the up-arrow key will cycle through previously entered statements.
UNIX shell commands
editA few common shell commands familiar to UNIX/Linux weenies are available within lush, in the form of functions. For example:
$ (ls)
=("pics" "todo.txt")
$ (cd "pics")
="/home/mkg/pics"
Control-key shortcuts
editSeveral keyboard shortcuts will be familiar to users of the bash shell or emacs:
- ctrl-a, ctrl-e: Go to the beginning/end of the line.
- ctrl-left, Ctrl-right: skip words.
Caret shortcuts
editSome functions have shortcuts of the form ^
_. These include:
- ^Afunction_name: Prints help about the function.
- ^Lsource_file.lsh : Loads and runs a source file (see "Running a source file" section below).
Remember not to put a space between the ^_
and its argument.
Running a source file
editA series of lush statements constitutes a lush program, and can be saved in a text file and run in batch mode: hello-world.lsh
; My first lush program
; Author: me
(print "Batch hello world")
To run hello-world.lsh from your shell, type:
mkg@FRIDGE:~$ lush hello-world.lsh
Alternately, you can run it from within the lush shell, using the libload
function:
$ (libload "hello-world.lsh")
The ".lsh" suffix is optional. Alternately, you can use libload
's caret shortcut ^L
:
$ ^Lhello-world.lsh
These filenames may also be tab-completed.
Just enough lisp to get by
editLush takes its list-based syntax straight from lisp. This section is for those with no lisp experience.
How lisp works
editAll lisp programs are a series of lists within lists, and the program proceeds by evaluating each list from left to right. This means that the first element in each list is treated as a function, and the rest of the elements are treated as the arguments. Once evaluated, a list returns a value (possibly ()
, the empty list).
(setq semester-grade (+ (* 0.2 (grade-homework homework-1))
(* 0.2 (grade-homework homework-2))
(* 0.6 (grade-exam final-exam))))
In the above example, the innermost lists (the ones starting with the grade-...
functions) evaluate out to homework and exam grades:
(setq semester-grade (+ (* 0.2 87)
(* 0.2 95)
(* 0.6 93)))
Their enclosing lists (starting with *
evaluate out to these grades scaled by some factor:
(setq semester-grade (+ 17.4
19
55.8))
The next enclosing list (starting with +
sums these scaled grades:
(setq semester-grade 92,2)
And the outermost list (setq ...)
takes this sum and assigns it to the variable "semester-grade
". It too returns a value, in this case the value of the assigned variable:
92.2
Creating lists
editAs shown above, all lists in lisp are evaluated. This can be unwelcome when you want to make a variable that is itself a list. Here's an example of a naive attempt at list creation:
(defparameter possible-baby-names ("Grendel" "Greta" "Garbanzo"))
Result:
*** "Grendel" : can't evaluate this list
Debug toplevel [y/N] ?
The problem is that we never intended the string "Grendel"
to be treated as a function, but lisp doesn't know that. There are two ways around this. One is to use the quote operator by putting a single ' before the list:
(defparameter my-list '("Grendel" "Greta" "Garbanzo"))
This works well enough in this case, where all the list elements are "literals", or expressions that can be used as-is, in a cut-and-paste manner. Sometimes, however, we want to put the results of calculations in our lists. In such a case, the above technique will produce the following undesired result:
(defparameter my-list '(1 2 (+ 1 2)))
(print my-list)
Result:
(1 2 (+ 1 2))
So the quote operator not only prevents the first element from being used as a function, it prevents all list items from being evaluated. Thus, the quote operator is useful primarily for moving around unevaluated code snippets rather than constructing lists as variables (see Macros).
What we want instead is to use the (list
list-constructor:
(defparamter my-list (list 1 2 (+ 1 2)))
(print my-list)
Result:
(1 2 3)
To summarize, use (list ...)
to construct lists, and use the quote operator to manipulate unevaluated snippets of code. You'll probably use (list ...)
most of the time.
List manipulation
editDiscuss car, cdr, cons and append
More
editFor the interested, there's more in the Lisp tricks section. These include things like passing around functions like variables, creating anonymous functions on the fly ("lambda functions"), and links to helpful lisp references.
Variables
editDeclaring global variables
editDeclaring and setting a global variable can be done using the defvar
or defparameter
command:
(defvar pi 3.14)
(defparameter time 20)
The two are identical except for one important detail: if a previous defvar
statement has already set the value of a symbol, a second defvar
on the same symbol name will do nothing. defparameter
, on the other hand, will always set the value as told:
(defvar pi "three point one four") ; does nothing!
(defparameter time 100000) ; time set to 100000
<syntaxhighlight>
<code>defvar</code> should therefore be used with caution, especially when running lush programs from within the lush shell. If you run a program with a <code>defvar</code> statement, Edit the file to change the variable's value, then re-run the program, the variable will remain unchanged.
=== Declaring local variables===
The <code>let*</code> and <code>let</code> statements can be used to declare local variables. It consists of a list of <code>(''variable_name'' ''initial_value'')</code> pairs, followed by a body of code that may use the declared variables:
<syntaxhighlight lang="lisp">
(let* ((''name_1'' ''value_1'') [(''name_2'' ''value_2'') ... (''name_n'' ''value_n'')])
''body'' )
The following example declares four local variables, then uses them to make curry:
(let* ((potato-var (new potato))
(carrot-var (new carrot))
(onion-var (new onion))
(roux-var (new roux))
(peel potato-var)
(chop carrot-var)
(dice onion-var)
(make-curry potato-var carrot-var onion-var))
let*
defines the variables in the order they're listed, while let
makes no such guarantees, allowing for possible parallelism. Unless you're sure that this is what you want, you should stick to let*
.
let*
/let
statements return the last expression evaluated, making them handy for setting up function calls with lots of arguments without cluttering the current scope with the argument variables. The following example creates a list for dinner, consisting of milk, rice, and curry:
(defparameter dinner (list "milk"
"rice"
(let* ((potato-var (new potato))
(carrot-var (new carrot))
(onion-var (new onion))
(roux-var (new roux))
(peel potato-var)
(chop carrot-var)
(dice onion-var)
(make-curry potato-var carrot-var onion-var))))
Setting values
editOnce declared, variable values can be changed using setq
from lisp. The following shows a numerical variable theta
being set to equal another variable pi
.
$ (setq theta pi)
As shown above, applying setq on numerical values sets the first variable to be an equal but separate copy of the second variable. As in lisp, this is also true for variables containing lists. Variables pointing to objects are another story; the variables themselves are like pointers in C/C++, where assignment just changes the value of the pointer.
Booleans and conditionals
editIn lush, as in lisp, empty and non-empty lists represent the boolean "true" and "false". You can also use the literals t
and nil
. Unlike lisp, lush has many objects other than lists, and these all evaluate to "true".
Numeric comparisons
editIf statements
editWhen statements
editBoolean gotchas
editThe dreaded t
edit
Lush inherits some historical baggage from lisp in the form of the "true" literal t
. Such a short literal name is bound to be used by the occasional careless newbie as a local variable, to represent time for example. While lush will prevent a user from creating a global variable named t
, a local variable named t
will likely cause subtle and silent errors.
Booleans in compiled Lush
editUnlike in C/C++, the integer value of 0 or pointer value of NULL
will not be considered 'false' in a lush conditional. To make a lush conditional understand an integer or C boolean expression, one must use the to-bool
function:
(when (to-bool #{ my_c_variable < 0 #} )
(print "my_c_variable is negative"))
Loops
editLush provides the traditional for and while loops, and more specialized functions for iterating over arrays.
For loops
editSyntax:
(for (''counter'' ''start_value'' ''end_value'' [''step_size''])
''body'')
Here is an example of printing the number from 0 through 10 using a for loop:
? (for (i 0 10 1)
(printf "%d " i))
0 1 2 3 4 5 6 7 8 9 10 = ()
The counter i
iterates through all values between 0 and 10 including 10. The loop expression returns the last expression evaluated within the loop.
You can specify the step size with an optional third argument:
? (for (i 10 0 -1)
(printf "%d " i))
10 9 8 7 6 5 4 3 2 1 0 = ()
While loops
editSyntax:
(while ''condition''
''body'')
While loops are useful for iterating through lists:
$ (defparameter todo-list (list "Wake up" "Shower" "Leave home" "Return to home" "Put on clothes"))
$ (while todo-list
(print (car todo-list))
(setq todo-list (cdr todo-list)))
"Wake up"
"Shower"
"Leave home"
"Return home"
"Put on clothes"
= todo-list
Like the for loop, the while loop returns the last expression evaluated in body.
Numeric array loops
editMost iterations will be done over elements of numerical arrays (vectors, matrices, or tensors). While one could do so using for
or while
loops, the following iterator functions provide a more convenient alternative.
idx-bloop
editidx-bloop
iterates through successive sub-arrays of an array by iterating through its first dimension. For example, the rows of a matrix can be printed as follows:
(defparameter mat [d[1 2 3][4 5 6]])
(idx-bloop ((row mat))
(print row))
Output:
[ 1.00 2.00 3.00 ]
[ 4.00 5.00 6.00 ]
You can iterate over multiple arrays simultaneously, so long as their first dimensions contain the same number of elements:
(defparameter mat (double-matrix 2 3))
(defparameter vec (double-matrix 2))
(idx-bloop ((row mat) (elem vec))
...)
idx-gloop
editidx-gloop
is an extension of idx-bloop
that provides a counter variable, as in a for
loop. Here is an idx-gloop
version of the row-printing example:
(defparameter mat [d[1 2 3][4 5 6]])
(idx-gloop ((row mat) (i))
(print "index:" i "row:" row))
</code>
Output:
<code>
"index:" 0 "row:" [ 1.00 2.00 3.00 ]
"index:" 1 "row:" [ 4.00 5.00 6.00 ]
idx-eloop
editidx-eloop
is simply an idx-bloop
that iterates over the last index rather than the first. For example, when applied to matrices, an idx-eloop
would iterate over its columns, not its rows.
Array iteration gotchas
edit- Unlike the
for
orwhile
loops, array loop expressions return the first array in the array list, not any of the expressions evaluated in the body. - The sub-arrays are allocated on the heap and should not be expected to persist outside of the loop, even when you assign them to an external symbol. Doing so can lead to fatal and mysterious memory errors.