Version 4 of Tcl and LISP

Updated 2001-12-04 09:54:27

Richard Suchenwirth - LISP (for LISt Processing) is one of the oldest computer languages, dating back to c. 1956, but still having a following (all of Emacs is configured in a LISP dialect) and evolution.

Though I like to play with other languages in Tcl, I never really bothered to "play LISP". Is it because LISP and Tcl are at least superficially so similar that there is little to give or take? Both languages treat program code like data, and build strongly on lists as major data structure. In LISP as in Tcl, a command is a list where the first element ("CAR") is the command name and the others are its arguments. LISP's "property lists" are a mapping from strings to lists, or what Tcl'ers know as an array.

So "playing LISP" would start with just some vocabulary exercises:

 proc car L {lindex $L 0}     ;# "catch address register"
 proc cdr L {lrange $L 1 end} ;# "catch data register"

LISP is more consequent in enclosing each and every list in parens, so you'll see much more of those in any LISP code (and typically have to close one or two handful at the end the file ;-). By adding the commonsense \n and even semicolon as command delimiters, Tcl can do with much less enclosing. Compare LISP's

 (PROGN
   (DO THIS)
   (DO THAT)
 )

to Tcl's

 { 
   do $this
   do $that
 }

But Tcl lists are not exactly as powerful as LISP's, since they are "just" flat sequences, not made up of cons cells (something like pairs of pointers) - so in LISP you can have mutable list parts, circular lists... Didn't miss that yet, however.

See also


KBK (27 Feb 2000) --

Oh, but of course you can have mutable lists. Take a look at the following code. (Of course, for it to not leak memory, we need anonymous lambdas that are garbage collected. Feather?)

 # Create a word to represent an anonymous lambda

 proc fwcons {} {
     variable cell
     if { [info exists cell] } {
         incr cell
     } else {
         set cell 1
     }
     return cell$cell
 }

 # LAMBDA - Create an anonymous function

 proc lambda { args body } {
     set p [fwcons]
     proc $p $args $body
     return $p
 }

 # CONS - Return a pair consisting of x and y.  Implemented as an anonymous
 #        function

 proc cons { x y } {
     lambda { a } [list if {$a} [list return $x] [list return $y]]
 }

Isn't the above an implementation of "COND", not "CONS"? - JCW

 # CAR/CDR - Dissect a pair by calling the anonymous function returned by CONS

 proc car { x } { $x 1 }
 proc cdr { x } { $x 0 }

 # NIL - The null list

 proc nil { a } { return nil }

 # NULL - Test if a list is empty

 proc null { l } { string equal nil $l }

 # MAP - Apply a function to each member of a list

 proc map { f l } {
     if { ! [null $l] } {
         $f [car $l]
         map $f [cdr $l]
     }
 }

 # PRIN1 - Print a thing with no newline

 proc prin1 { thing } {
     puts -nonewline $thing
     puts -nonewline { }
 }

 # PRINTLIST - Print each element of a list

 proc printlist { l } {
     map prin1 $l
     puts {}
 }

 # RPLACA - Replace the CAR of a list

 proc rplaca { l x } {
     proc $l [info args $l] [lreplace [info body $l] 2 2 [list return $x]]
     return $l
 }

 # RPLACD - Replace the CDR of a list

 proc rplacd { l y } {
     proc $l [info args $l] [lreplace [info body $l] 3 3 [list return $y]]
     return $l
 }

 set list [cons a [cons b [cons c nil]]]
 prin1 {Before:}
 printlist $list

 rplaca $list d
 rplacd [cdr [cdr $list]] [cons a nil]
 prin1 {After:}
 printlist $list

Arts and crafts of Tcl-Tk programming