Introduction to newLISP/More examples

= More examples =

This section contains some simple examples of newLISP in action. You can find plenty of good newLISP code on the web and in the standard newLISP distribution.

On your own terms
You might find that you don't like the names of some of the newLISP functions. You can use constant and global to assign another symbol to the function:

You can now use set! instead of setf. There's no speed penalty for doing this.

It's also possible to define your own alternatives to built-in functions. For example, earlier we defined a context and a default function that did the same job as println, but kept a count of how many characters were output. For this code to be evaluated rather than the built-in code, do the following.

First, define the function:

Keep the original newLISP version of println available by defining an alias for it:

Assign the println symbol to your Output function:

Now you can use println as usual:

2 3 4 5 6 7 8 9 10 11

1 2 3 4 5

And it appears to do the same job as the original function. But now you can also make use of the bonus features of the alternative println that you defined:

In case you've been counting carefully - the counter has been counting the length of the arguments supplied to the Output function. These include the parentheses, of course...

Using a SQLite database
Sometimes it's easier to use existing software rather than write all the routines yourself, even though it can be fun designing something from the beginning. For example, you can save much time and effort by using an existing database engine such as SQLite instead of building custom data structures and database access functions. Here's how you might use the SQLite database engine with newLISP.

Suppose you have a set of data that you want to analyse. For example, I've found a list of information about the elements in the periodic table, stored as a simple space-delimited table:

(You can find the list in this file on GitHub..)

The columns here are Atomic Weight, Melting Point, Boiling Point, Density, Percentage in the earth's crust, Year of discovery, Group, and Ionization energy. (I used 0 to mean Not Applicable, which was not a very good choice, as it turns out).

To load newLISP's SQLite module, use the following line:

This loads in the newLISP source file which contains the SQLite interface. It also creates a new context called sql3, containing the functions and symbols for working with SQLite databases.

Next, we want to create a new database or open an existing one:

This creates a new SQLite database file called periodic_table, and opens it. If the file already exists, it will be opened ready for use. You don't have to refer to this database again, because newLISP's SQLite library routines maintain a current database in the sql3 context. If the open function fails, the most recent error stored in sql3:error is printed instead.

I've just created this database, so the next step is to create a table. First, though, I'll define a symbol containing a string of column names and the SQLite data types each one should use. I don't have to do this, but it probably has to be written down somewhere, so, instead of the back of an envelope, write it in a newLISP symbol:

Now I can make a function that creates the table:

It's easy because I've just created the column-def symbol in exactly the right format! This function uses the sql3:sql function to create a table called t1.

I want one more function: one that fills the SQLite table with the data stored in the list elements. It's not a pretty function, but it does the job, and it only has to be called once.

This function calls parse twice. The first parse breaks the data into lines. The second parse breaks up each of these lines into a list of fields. Then I can use format to enclose the value of each field in single quotes, remembering to change the strings to integers or floating-point numbers (using int and float) according to the column definitions.

It's now time to build the database:

- if the t1 table doesn't exist in a list of tables, the functions that create and fill it are called.

Querying the data
The database is now ready for use. But first I'll write a simple utility function to make queries easier:

This function submits the supplied text, and either prints out the results (by mapping println over the results list) or displays the error message.

Here are a few sample queries.

Find all elements discovered before 1900 that make up more than 2% of the earth's crust, and display the results sorted by their discovery date:

("Iron" 5.05 0) ("Magnesium" 2.08 1755) ("Oxygen" 46.71 1774) ("Potassium" 2.58 1807) ("Sodium" 2.75 1807) ("Calcium" 3.65 1808) ("Silicon" 27.69 1824) ("Aluminium" 8.07 1825)

When were the noble gases discovered (they are in group 18)?

("He" "Helium" 1895) ("Ne" "Neon" 1898) ("Ar" "Argon" 1894) ("Kr" "Krypton" 1898) ("Xe" "Xenon" 1898) ("Rn" "Radon" 1900)

What are the atomic weights of all elements whose symbols start with A?

("Actinium" "Ac" 227) ("Aluminium" "Al" 26.9815) ("Americium" "Am" 243) ("Argon" "Ar" 39.948) ("Arsenic" "As" 74.9216) ("Astatine" "At" 210) ("Gold" "Au" 196.9665) ("Silver" "Ag" 107.8682)

It's elementary, my dear Watson! Perhaps the scientists out there can supply some examples of more scientifically interesting queries?

You can find MySQL and Postgres modules for newLISP on the net as well.

Simple countdown timer
Next is a simple countdown timer that runs as a command-line utility. This example shows some techniques for accessing the command-line arguments in a script.

To start a countdown, you type the command (the name of the newLISP script) followed by a duration. The duration can be in seconds; minutes and seconds; hours, minutes, and seconds; or even days, hours, minutes, and seconds, separated by colons. It can also be any newLISP expression.

> countdown 30 Started countdown of 00d 00h 00m 30s at 2006-09-05 15:44:17 Finish time:      2006-09-05 15:44:47 Elapsed: 00d 00h 00m 11s Remaining: 00d 00h 00m 19s

or:

> countdown 1:30 Started countdown of 00d 00h 01m 30s at 2006-09-05 15:44:47 Finish time:      2006-09-05 15:46:17 Elapsed: 00d 00h 00m 02s Remaining: 00d 00h 01m 28s

or:

> countdown 1:00:00 Started countdown of 00d 01h 00m 00s at 2006-09-05 15:45:15 Finish time:      2006-09-05 16:45:15 Elapsed: 00d 00h 00m 02s Remaining: 00d 00h 59m 58s

or:

> countdown 5:04:00:00 Started countdown of 05d 04h 00m 00s at 2006-09-05 15:45:47 Finish time:      2006-09-10 19:45:47 Elapsed: 00d 00h 00m 05s Remaining: 05d 03h 59m 55s

Alternatively, you can supply a newLISP expression instead of a numerical duration. This might be a simple calculation, such as the number of seconds in π minutes:

> countdown "(mul 60 (mul 2 (acos 0)))" Started countdown of 00d 00h 03m 08s at 2006-09-05 15:52:49 Finish time:      2006-09-05 15:55:57 Elapsed: 00d 00h 00m 08s Remaining: 00d 00h 03m 00s

or, more usefully, a countdown to a specific moment in time, which you supply by subtracting the time now from the time of the target:

> countdown "(- (date-value 2006 12 25) (date-value))" Started countdown of 110d 08h 50m 50s at 2006-09-05 16:09:10 Finish time:       2006-12-25 00:00:00 Elapsed: 00d 00h 00m 07s Remaining: 110d 08h 50m 43s

- in this example we've specified Christmas Day using date-value, which returns the number of seconds since 1970 for specified dates and times.

The evaluation of expressions is done by eval-string, and here it's applied to the input text if it starts with "(" - generally a clue that there's a newLISP expression around! Otherwise the input is assumed to be colon-delimited, and is split up by parse and converted into seconds.

The information is taken from the arguments given on the command line, and extracted using main-args, which is a list of the arguments that were used when the program was run:

This fetches argument 2; argument 0 is the name of the newLISP program, argument 1 is the name of the script, so argument 2 is the first string following the countdown command.

Save this file as countdown, and make it executable.

Editing text files in folders and hierarchies
Here's a simple function that updates some text date stamps in every file in a folder, by looking for enclosing tags and changing the text between them. For example, you might have a pair of tags holding the date the file was last edited, such as  and .

which can be called like this:

The replace-string-in-files function accepts a folder name. The first task is to extract a list of suitable files - we're using directory with the regular expression {^[^.]} to exclude all files that start with a dot. Then, for each file, the contents are loaded into a symbol, the replace function replaces text enclosed in the specified strings, and finally the modified text is saved back to disk. To call the function, specify the start and end tags, followed by the text and the folder name. In this example we're just using a simple ISO date stamp provided by date and date-value.

Recursive version
Suppose we now wanted to make this work for folders within folders within folders, ie to traverse a hierarchy of files, changing every file on the way down. To do this, re-factor the replace-string function so that it works on a passed pathname. Then write a recursive function to look for folders within folders, and generate all the required pathnames, passing each one to the replace-string function. This re-factoring might be a good thing to do anyway: it makes the first function simpler, for one thing.

Next for that recursive tree-walking function. This looks at each normal entry in a folder/directory, and tests to see if it's a directory (using directory?). If it is, the replace-in-tree function calls itself and starts again at the new location. If it isn't, the pathname of the file is passed to the replace-string-in-file function.

To change a tree-full of files, call the function like this:

It's important to test these things in a scratch area first; a small mistake in your code could make a big impact on your data. Caveat newLISPer!

Talking to other applications (MacOS X example)
newLISP provides a good environment for gluing together features found in application programs that have their own scripting languages. It's fast and small enough to keep out of the way of the other components of a scripted solution, and it's good for processing information as it passes through your workflow.

Here is an example of how you can use a newLISP script to send non-newLISP scripting commands to applications. The task is to construct a circle in Adobe Illustrator, given three points on the circumference.

The solution is in three parts. First, we obtain the coordinates of the selection from the application. Next we calculate the radius and centre point of the circle that passes through these points. Finally, we can draw the circle. The first and final parts use AppleScript, which is run using the osascript command, because Adobe Illustrator doesn't understand any other scripting language (on Windows, you use Visual Basic rather than AppleScript).



The calculations and general interfacing are carried out using newLISP. This can often be a better solution than using native AppleScript, because newLISP offers many powerful string and mathematical functions that can't be found in the default AppleScript system. For example, if I want to use trigonometry I would have to find and install an extra component - AppleScript doesn't provide any trig functions at all.

The newLISP script can sit in the Scripts menu on the menu bar; put it into the Library > Scripts > Applications > Adobe Illustrator folder, which accepts both text files and AppleScripts). It's then ready to be selected while you're working in Illustrator. To use it, just select a path with at least three points, then run the script. The first three points define the location for the new circle.

There's hardly any error handling in this script! More should definitely be added to the first stage (because the selection might not be suitable for subsequent processing).