[plt-scheme] Re: Lisp mentality vs. Python mentality

From: Ciprian Dorin, Craciun (ciprian.craciun at gmail.com)
Date: Sat Apr 25 03:24:18 EDT 2009

On Sat, Apr 25, 2009 at 9:06 AM, Carl Banks <pavlovevidence at gmail.com> wrote:
> In answering the recent question by Mark Tarver, I think I finally hit
> on why Lisp programmers are the way they are (in particular, why they
> are often so hostile to the "There should only be one obvious way to
> do it" Zen).
>
> Say you put this task to a Lisp and a Python programmer: Come up with
> a good, generic, reusable way to compare two lists.  What are their
> respective trains of thought?
>
>
> Lisp programmer:
>
> Well, there is a standard function called mismatch that does it, but I
> can't recommend it.  First of all, you don't know where that
> function's been.  Anyone and their mother could have worked on it, did
> they have good, sound programming practice in mind when they wrote
> it?  Of course not.  Let's be real here, we have to implement this by
> hand.
>
> (defun lists-are-equal (a b)
>   (or (and (not a) (not b))
>       (and (= (car a) (car b)) (lists-are-equal (cdr a) (cdr b))))
>
> There, much better than the standard function, and better yet, it's in
> the *absolute minimal form possible*.  There is no way to express list
> comparison in a more reduced form.  It's almost erotic how awesome it
> is.  I'm---whoa, ok, I'm getting a little excited now, settle down.
> Well, come to think of it, that's really not that good.  First of all
> it's a function.  I mean, it just sits there and does nothing till you
> call it.  How boring is that?  It can't react to the current
> situation.  Plus it compares all lists the same way, and that is
> really inefficient.  Every list compare is a new problem.  Different
> lists need different comparative strategies.  This function simply
> won't do.  I need a macro that can intelligently compile the right
> list compare methodology in.  For instance, if we want to compare two
> lists that are known at compile time, we don't want to waste time
> comparing them at runtime.  No, the macro must detect constant
> arguments and special case them.  Good start.  Now, we have to
> consider the conditions this comparison is being done under.  If the
> user is passing the result of a sort to this macro, it's almost
> certain that they are trying to see whether the lists have the same
> elements.  We can do that a lot more efficiently with a countset.  So
> let's have the macro check to see if the forms passed to it are all
> sort calls.  Better yet, let's check for my own powerful sort macro.
> Hmm.  Wait... I think my 4600-line sort macro already checks its
> calling context to see if its results are being fed to a list
> comparison.  I'll have to refactor that together with this macro.  Ok,
> good, now I am sure other users will eventually want to customize list
> comparison for their own use, after all every list comparison is
> different and I can't possibly anticipate all of them.  A user needs
> to be able to adapt to the situation, so it's vitally important to
> create a plug-in infrastructure to give them that flexibility.  Now,
> what about exceptions, there's a millions ways to deal with that...
>
> ...and so on until eyelids can no longer stay open....
>
>
>
> Python programmer:
>
> a == b.  Next question.
>
>
>
> Carl Banks, who might be exaggerating
>
> ...a little.
> --
> http://mail.python.org/mailman/listinfo/python-list


    Well, if you opened the Pandora's box, lets see how can we
implement this both ways...

The Scheme way (a little more verbose but clearer I would say):
--------
(define (compare a b (comp eq?))
    (cond
        ((and (null? a) (null? b) #t))
        ((or (null? a) (null? b) #f))
        ((and (comp (first a) (first b)))
            (compare (rest a) (rest b) comp))
        (else #f)))

(compare '(1 2 3) '(1 2 3))
(compare '(1 "a" 3) '(1 "a" 3) equal?)
(compare '(1 2 3) '(1 2 4) <=)
---------

Python way:
---------
def eq (a, b) :
    return a == b

def compare (a, b, comp = eq) :
    if len (a) != len (b) :
        return False
    for i in xrange (len (a)) :
        if not comp (a[i], b[i]) :
            return False
    return True

compare ([1, 2, 3], [1, 2, 3])
---------

    I don't see another way... (Or at least another more different way...)

    Which is best? None:
    * they both work in linear time (except the Python implementation
checks the length first);
    * they are both compact and readable;
    * they are both expandable;

    Personally I like best the Scheme version, because for example I
can use it like (compare '(1 2 3) '(1 2 4) <=) to compare for less or
equal in case of vectors. In the case of Python I would have needed to
write compare ([1, 2, 3], [1, 2, 4], lambda a, b : a <= b)

    Ciprian Craciun.

    P.S.: I think of this as a challenge for hackers in both languages
to come up with the most estetic, optimal and useful implementation...
(My implementations are just toys...)


Posted on the users mailing list.