Jump to content

Lush/Beginning Lush

From Wikibooks, open books for an open world

This chapter covers the basic syntax of lush. For discussion of classes, see Classes and Objects.

Getting Started

[edit | edit source]

Like 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

[edit | edit source]

To 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

[edit | edit source]

There are some usability features built into the lush shell:

Online help

[edit | edit source]

The 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

[edit | edit source]

If 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

[edit | edit source]

Hitting the up-arrow key will cycle through previously entered statements.

UNIX shell commands

[edit | edit source]

A 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

[edit | edit source]

Several 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

[edit | edit source]

Some 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

[edit | edit source]

A 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

[edit | edit source]

Lush takes its list-based syntax straight from lisp. This section is for those with no lisp experience.

How lisp works

[edit | edit source]

All 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

[edit | edit source]

As 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

[edit | edit source]

Discuss car, cdr, cons and append

For 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

[edit | edit source]

Declaring global variables

[edit | edit source]

Declaring 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

[edit | edit source]

Once 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

[edit | edit source]

In 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

[edit | edit source]

If statements

[edit | edit source]

When statements

[edit | edit source]

Boolean gotchas

[edit | edit source]

The dreaded t

[edit | edit source]

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

[edit | edit source]

Unlike 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

[edit | edit source]

Lush provides the traditional for and while loops, and more specialized functions for iterating over arrays.

For loops

[edit | edit source]

Syntax:

 (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

[edit | edit source]

Syntax:

 (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

[edit | edit source]

Most 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

[edit | edit source]

idx-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

[edit | edit source]

idx-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

[edit | edit source]

idx-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 | edit source]
  • Unlike the for or while 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.