Python syntax in Lisp and Scheme

Erann Gat my-first-name.my-last-name at jpl.nasa.gov
Fri Oct 3 19:46:01 EDT 2003
In article <92c59a2c.0310031345.57d20631 at posting.google.com>,
jcb at iteris.com (MetalOne) wrote:

> kalath at lycos.com (Mark Brady) wrote in message
news:<e840346c.0310030302.6be0c378 at posting.google.com>...
> > Personally I find Scheme and Common Lisp easier to read but that's
> > just me, I prefer S-exps ...
> 
> I am just barely familiar with Lisp and Scheme.  However, I always
> find comments like the above interesting.  I have seen other people
> make this claim also.
> However, from an earlier post on comp.lang.python comparing a simple
> loop.
> 
> Scheme
> (define vector-fill!
>   (lambda (v x)
>     (let ((n (vector-length v)))
>       (do ((i 0 (+ i 1)))
>           ((= i n))
>           (vector-set! v i x)))))
> 
> Python
> def vector_fill(v, x):
>     for i in range(len(v)):
>         v[i] = x
> 
> To me the Python code is easier to read, and I can't possibly fathom
> how somebody could think the Scheme code is easier to read.  It truly
> boggles my mind.

In Common Lisp you can write:

(defun vector-fill (v x)
  (loop for i from 0 below (length v) do
        (setf (aref v i) x)))

or

(defun vector-fill (v x)
  (dotimes (i (length v))
    (setf (aref v i) x)))

But if you focus on examples like this you really miss the point.  Imagine
that you wanted to be able to write this in Python:

def vector_fill(v, x):
  for i from 0 to len(v)-1:
    v[i] = x

You can't do it because Python doesn't support "for i from ... to ...",
only "for i in ...".  What's more, you can't as a user change the language
so that it does support "for i from ... to ...".  (That's why the xrange
hack was invented.)

In Lisp you can.  If Lisp didn't already have LOOP or DOTIMES as part of
the standard you could add them yourself, and the way you do it is by
writing a macro.

That's what macros are mainly good for, adding features to the langauge in
ways that are absolutely impossible in any other language.  S-expression
syntax is the feature that enables users to so this quickly and easily.

> I can't see
> why a LISP programmer would even want to write a macro.

That's because you are approaching this with a fundamentally flawed
assumption.  Macros are mainly not used to make the syntax prettier
(though they can be used for that).  They are mainly used to add features
to the language that cannot be added as functions.

For example, imagine you want to be able to traverse a binary tree and do
an operation on all of its leaves.  In Lisp you can write a macro that
lets you write:

(doleaves (leaf tree) ...)

You can't do that in Python (or any other langauge).

Here's another example of what you can do with macros in Lisp:

(with-collector collect
  (do-file-lines (l some-file-name)
    (if (some-property l) (collect l))))

This returns a list of all the lines in a file that have some property. 
DO-FILE-LINES and WITH-COLLECTOR are macros, and they can't be implemented
any other way because they take variable names and code as arguments.

E.


----

P.S.  Here is the code for WITH-COLLECTOR and DO-FILE-LINES:

(defmacro with-collector (var &body body)
  (let ( (resultvar (gensym "RESULT")) )
    `(let ( (,resultvar '()) )
       (flet ( (,var (item) (push item ,resultvar)) )
         , at body)
       (nreverse ,resultvar))))

(defmacro do-file-lines ((linevar filename &optional streamvar) &body body)
  (let ( (streamvar (or streamvar (gensym "S"))) )
    `(with-open-file (,streamvar ,filename)
       (do ( (,linevar (read-line ,streamvar nil nil)
                       (read-line ,streamvar nil nil)) )
           ( (null ,linevar) )
         , at body))))

Here's DOLEAVES:

(defmacro doleaves ((var tree) &body body)
  `(walkleaves (lambda (,var) , at body) ,tree))

:-)

(defun walkleaves (fn tree)
  (iterate loop1 ( (tree tree) )
    (if (atom tree)
      (funcall fn tree)
      (progn (loop1 (car tree)) (and (cdr tree) (loop1 (cdr tree)))))))

; This is the really cool way to iterate
(defmacro iterate (name args &rest body)
  `(labels ((,name ,(mapcar #'car args) , at body))
     (,name ,@(mapcar #'cadr args))))

E.




More information about the Python-list mailing list