Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Lisp mentality vs. Python mentality

840 views
Skip to first unread message

Carl Banks

unread,
Apr 25, 2009, 2:06:30 AM4/25/09
to
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.

Emmanuel Surleau

unread,
Apr 25, 2009, 2:23:00 AM4/25/09
to pytho...@python.org

Ah well. I guess this explains Emacs.

Cheers,

Emm

Lawrence D'Oliveiro

unread,
Apr 25, 2009, 2:47:26 AM4/25/09
to
In message <mailman.4540.1240640...@python.org>, Emmanuel
Surleau wrote:

> Ah well. I guess this explains Emacs.

So how would you explain someone who uses Emacs to write Python code? Like
me?

Ciprian Dorin, Craciun

unread,
Apr 25, 2009, 3:24:18 AM4/25/09
to Carl Banks, pytho...@python.org, plt-s...@list.cs.brown.edu
> --
> 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...)

John Yeung

unread,
Apr 25, 2009, 3:36:24 AM4/25/09
to
On Apr 25, 2:06 am, Carl Banks <pavlovevide...@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).

I don't get that impression from Lisp programmers. I suppose it's
only fair that I disclose (1) I admire Lisp, especially Scheme, (2) I
hardly know Lisp at all, and (3) I don't frequent any Lisp forums/
newsgroups/etc.

I do get the impression that Lispers tend to feel Lisp is superior to
all other languages, and I agree that in some ways it is. I don't
think most Lispers' main objection to Python is about "only one
obvious way" but rather things like the limitations, compromises, and
impurities in the language. Certainly compared to Scheme, Python
sacrifices a lot of purity for practicality. (And I guess some fans
of Scheme would argue that Common Lisp does the same!)

Ultimately, Lisp is first and foremost academic (Scheme especially so)
while Python is first and foremost practical. I think Paul Graham's
essays on Lisp exemplify the Lisp mentality.

The kind of Lisp programmer you go on to describe (escalating a simple
problem into a gigantic framework) sounds to me very much like plenty
of Python programmers. A lot of beginners in this very newsgroup ask
"can I just do this simple thing?" and get responses like "well, that
does handle what you need it to, but what if your data is this?
Better add some checking or exception handling. Also, you could make
it run faster by tripling the amount of code as follows..." etc.

John

bearoph...@lycos.com

unread,
Apr 25, 2009, 3:43:21 AM4/25/09
to
Ciprian Dorin, Craciun:

> 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

That's not "pythonic".

Bye,
bearophile

Ciprian Dorin, Craciun

unread,
Apr 25, 2009, 3:50:50 AM4/25/09
to bearoph...@lycos.com, pytho...@python.org
> --
> http://mail.python.org/mailman/listinfo/python-list

Ok... Then what's pythonic? Please give a pythonic implementation...

Ciprian Craciun.

P.S.: Also, I'm tired of hearing about the pythonic way... Where
do I find a definitive description about the pythonic way? I think
that this word is used only when someone sees something that he
doesn't like, he doesn't know what he doesn't like at it, and just
goes to say its un-pythonic, without saying what would be... Wouldn't
be just easier to say "I don't know" or "I doesn't feel right to me"?

Neil Van Dyke

unread,
Apr 25, 2009, 3:56:46 AM4/25/09
to Ciprian Dorin, Craciun, pytho...@python.org, plt-s...@list.cs.brown.edu
I believe this thread is off-topic for the "plt-scheme" list.
Please cease cross-posting.

Paul Rubin

unread,
Apr 25, 2009, 4:03:54 AM4/25/09
to
Carl Banks <pavlove...@gmail.com> writes:
> Python programmer:
>
> a == b. Next question.

in lisp you'd use (equal a b)

Paul Rubin

unread,
Apr 25, 2009, 4:04:16 AM4/25/09
to
"Ciprian Dorin, Craciun" <ciprian...@gmail.com> writes:
> Ok... Then what's pythonic? Please give a pythonic implementation...

Use the builtin a==b, similar to (equal a b)

Ciprian Dorin, Craciun

unread,
Apr 25, 2009, 4:10:29 AM4/25/09
to pytho...@python.org

But how about extensibility?

"Martin v. Löwis"

unread,
Apr 25, 2009, 4:30:56 AM4/25/09
to
>>> Ok... Then what's pythonic? Please give a pythonic implementation...
>> Use the builtin a==b, similar to (equal a b)
>
> But how about extensibility?

== is extensible. To compare two things for equality, use ==.

This is what Carl Banks referred in his original posting. You just
*don't* implement a function that compares two lists, not even as
an exercise for estetic, optimal, and useful implementations -
because you'll know that it won't be useful, anyway, if you can already
use the builtin == in the first place.

I see that you allow for a different comparison function. I do wonder
what the use case for this is - in what application do you have to
compare two lists for equality, and the item's __eq__ is inappropriate?
What would break if you fix the item's __eq__, instead of writing
your own comparison algorithm?

If I would ever run into such a case, I would inline the loop, rather
than writing a function that receives a callable:

for index, v1 in enumerate(l1):
if not comp(v1, l2[index]):
res = False
break
else:
res = True

Regards,
Martin

Ben Finney

unread,
Apr 25, 2009, 4:34:50 AM4/25/09
to
Carl Banks <pavlove...@gmail.com> writes:

> 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).

That's not what the Zen says. The statement you're mis-quoting says,
minus the parenthetical:

There should be one obvious way to do it.

It's only in a parenthetical (“and preferably only one”) that the word
“only” appears. The emphasis is not on having only one way, but on
having one *obvious* way.

--
\ “The optimist thinks this is the best of all possible worlds. |
`\ The pessimist fears it is true.” —J. Robert Oppenheimer |
_o__) |
Ben Finney

Ben Finney

unread,
Apr 25, 2009, 4:36:27 AM4/25/09
to
"Ciprian Dorin, Craciun" <ciprian...@gmail.com> writes:

> On Sat, Apr 25, 2009 at 11:04 AM, Paul Rubin
> <http://phr...@nospam.invalid> wrote:
> > Use the builtin a==b, similar to (equal a b)
>
> But how about extensibility?

Easily extensible. If you want types that compare in different ways,
just define your new type so that the comparison methods do what you
want them to do.

--
\ “It is the fundamental duty of the citizen to resist and to |
`\ restrain the violence of the state.” —Noam Chomsky, 1971 |
_o__) |
Ben Finney

"Martin v. Löwis"

unread,
Apr 25, 2009, 4:36:21 AM4/25/09
to John Yeung
> I don't get that impression from Lisp programmers.

I observe a phenomenon on language fanatics, no matter what language,
but in particular for the less-than-mainstream language: the desire
to formulate trivial algorithms over and over again, just to enjoy the
beauty of the result (and sometimes the mere fact of being able to do
so).

I suppose people writing real applications in LISP have better things
to spend time on.

Regards,
Martin

Ciprian Dorin, Craciun

unread,
Apr 25, 2009, 4:44:14 AM4/25/09
to Martin v. Löwis, pytho...@python.org
On Sat, Apr 25, 2009 at 11:30 AM, "Martin v. Löwis" <mar...@v.loewis.de> wrote:
>>>>     Ok... Then what's pythonic? Please give a pythonic implementation...
>>> Use the builtin a==b, similar to (equal a b)
>>
>>     But how about extensibility?
> [...]

>
> I see that you allow for a different comparison function. I do wonder
> what the use case for this is - in what application do you have to
> compare two lists for equality, and the item's __eq__ is inappropriate?
> What would break if you fix the item's __eq__, instead of writing
> your own comparison algorithm?
>
> [...]

A practical example: I have lists that contain strings, but I want
to compare them in an case-insensitive way... Should I update the
__eq__ method (for str class) and break almost everything? Can I write
now a == b? Nop... I need the loop you've just mentioned in all the
places where the comparison changes just in the operator, not in the
algorithm... (I would say this is bad coding practice...)

marek...@wp.pl

unread,
Apr 25, 2009, 4:48:29 AM4/25/09
to
Ciprian Dorin, Craciun napisał(a):
> Python way:

> 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
This is shorter, but I'm not sure if more pythonic:
def compare(a, b, compfunc=eq):
return all(compfunc(aelem, belem) for aelem, belem in zip_longest
(a, b, fillvalue=object()))

Regards,
Marek

Ciprian Dorin, Craciun

unread,
Apr 25, 2009, 4:52:24 AM4/25/09
to Martin v. Löwis, pytho...@python.org

Yes, you're right, Lisp people have better things to do, because
these algorithms are already implemented for them: just see ormap and
andmap functions that do just what we wanted...
http://download.plt-scheme.org/doc/html/reference/pairs.html#(def._((quote._~23~25kernel)._ormap))
http://download.plt-scheme.org/doc/html/reference/pairs.html#(def._((lib._scheme/private/map..ss)._andmap))

Ciprian.

P.S.: I'm not a language fanatic... I programmed both in Python
and Scheme (mostly in Python)...

P.P.S.: I'm just trying to see why is Python better than Lisp or
vice-versa... I would say they are almost the same: both usable for
day-to-day real world applications...

Paul Rubin

unread,
Apr 25, 2009, 4:57:18 AM4/25/09
to
"Ciprian Dorin, Craciun" <ciprian...@gmail.com> writes:
> > Use the builtin a==b, similar to (equal a b)
> But how about extensibility?

"a==b" is like (equal a b). "a is b" is like (eq a b).

Paul Rubin

unread,
Apr 25, 2009, 5:00:05 AM4/25/09
to
"Ciprian Dorin, Craciun" <ciprian...@gmail.com> writes:
> A practical example: I have lists that contain strings, but I want
> to compare them in an case-insensitive way... Should I update the
> __eq__ method (for str class) and break almost everything? Can I write
> now a == b? Nop... I need the loop you've just mentioned in all the
> places where the comparison changes just in the operator, not in the
> algorithm... (I would say this is bad coding practice...)

In Lisp I think you'd use (equal (mapcar upcase a) (mapcar upcase b))
or something like that. In Python, a.upper() == b.upper().

Really, Python uses its object system more heavily than old-school
Lisp did, but in other regards (despite the howls of partisans on both
sides) they are really not that different from each other.

Paul Rubin

unread,
Apr 25, 2009, 5:02:34 AM4/25/09
to
"Ciprian Dorin, Craciun" <ciprian...@gmail.com> writes:
> P.P.S.: I'm just trying to see why is Python better than Lisp or
> vice-versa... I would say they are almost the same: both usable for
> day-to-day real world applications...

Python tries to be simple and pragmatic while not aiming for as
heavy-duty applications as Common Lisp. Scheme is more of a research
language that's way past its prime. If you like Scheme, you should
try Haskell. Python has the motto "practicality beats purity".
With Haskell, it's exactly the opposite ;-).

"Martin v. Löwis"

unread,
Apr 25, 2009, 6:55:58 AM4/25/09
to Ciprian Dorin, Craciun, pytho...@python.org
> A practical example: I have lists that contain strings, but I want
> to compare them in an case-insensitive way...

I'd claim that this is still theoretical: what are these strings, and
why do you have lists of them that you want to compare?

Why don't you try to lower-case the strings in the list as you
add them?

Also, are you sure a list is the best data structure for your problem?
Perhaps using a set would be better?

> Should I update the
> __eq__ method (for str class) and break almost everything? Can I write
> now a == b? Nop... I need the loop you've just mentioned in all the
> places where the comparison changes just in the operator, not in the
> algorithm... (I would say this is bad coding practice...)

If you want to compare the same lists in many places, this is indeed
bad coding practice. You should try to centralize whatever reasons
you have for comparing the lists into a few methods of the objects
holding the lists.

Regards,
Martin

Ciprian Dorin, Craciun

unread,
Apr 25, 2009, 8:32:21 AM4/25/09
to Martin v. Löwis, pytho...@python.org
On Sat, Apr 25, 2009 at 1:55 PM, "Martin v. Löwis" <mar...@v.loewis.de> wrote:
>>     A practical example: I have lists that contain strings, but I want
>> to compare them in an case-insensitive way...
>
> I'd claim that this is still theoretical: what are these strings, and
> why do you have lists of them that you want to compare?
>
> Why don't you try to lower-case the strings in the list as you
> add them?
>
> Also, are you sure a list is the best data structure for your problem?
> Perhaps using a set would be better?

Indeed the example I've given is purely theoretical. But still, I
could find a use case for such a thing: just imagine we are building a
small shell-like application that reads one line (the commands),
splits it by spaces and always expects to have 4 elements and that
each respects a given regular expression, one specific for each index.
In this case I could syntactically check for correctness by doing
this:

compare (regular_expressions, splitted_line, re.match)

Of course I could have just created a big regular expression for
the entire line. But maybe my 4 elements come from variables obtained
from a web-server query, or the regular expressions are not static but
dynamically generated at run-time.


>> Should I update the
>> __eq__ method (for str class) and break almost everything? Can I write
>> now a == b? Nop... I need the loop you've just mentioned in all the
>> places where the comparison changes just in the operator, not in the
>> algorithm... (I would say this is bad coding practice...)
>
> If you want to compare the same lists in many places, this is indeed
> bad coding practice. You should try to centralize whatever reasons
> you have for comparing the lists into a few methods of the objects
> holding the lists.
>
> Regards,
> Martin

I like object oriented programming, but most of the times we are
just throwing together code and data even when the data has no
behavior and the code is in fact just one possible processing
algorithm. Like in the case you are mentioning, if I tie the
comparison code to the actual data structure, then I'll never be able
to reuse it... But if I leave the code as a standalone function, and
just use the data (or any data that resembles the original structure)
then maybe I'll be able to reuse it...

Ciprian.

"Martin v. Löwis"

unread,
Apr 25, 2009, 9:01:28 AM4/25/09
to Ciprian Dorin, Craciun, pytho...@python.org
> Indeed the example I've given is purely theoretical. But still, I
> could find a use case for such a thing: just imagine we are building a
> small shell-like application that reads one line (the commands),
> splits it by spaces and always expects to have 4 elements and that
> each respects a given regular expression, one specific for each index.
> In this case I could syntactically check for correctness by doing
> this:
>
> compare (regular_expressions, splitted_line, re.match)
>
> Of course I could have just created a big regular expression for
> the entire line. But maybe my 4 elements come from variables obtained
> from a web-server query, or the regular expressions are not static but
> dynamically generated at run-time.

Ok, in this case I would write a function:

def validate_commandline(rexes, line):
if len(rexes) != len(line):
raise ValueError("Incorrect number of arguments, expected %d,"
"got %d" % (len(rexes), len(line)))
for i in range(len(line)):
if not re.match(rexes[i], line[i]):
raise ValueError, "Incorrect argument %d" % i

IOW, in this specific case, I would not only want a true/false result,
but also an indication of the actual error to report to the user.
Your universal compare function would be no good here.

Regards,
Martin

Ciprian Dorin, Craciun

unread,
Apr 25, 2009, 9:11:35 AM4/25/09
to Martin v. Löwis, pytho...@python.org


Well in fact I would have written it like:

def validate_commandline(rexes, line) :
if not compare (rexes, line, re.match) :
if len (rexes) != len (line) :
raise ValueError ("mismatch len")
mismatch = find_index (rexes, line, re.match, negate = True)
raise ValueError ("mismatch at %d" % (mismatch))

Assuming, that I would have the function find_index.

Ciprian.

Hrvoje Niksic

unread,
Apr 25, 2009, 10:23:31 AM4/25/09
to
Paul Rubin <http://phr...@NOSPAM.invalid> writes:

> "Ciprian Dorin, Craciun" <ciprian...@gmail.com> writes:
>> A practical example: I have lists that contain strings, but I want
>> to compare them in an case-insensitive way... Should I update the
>> __eq__ method (for str class) and break almost everything? Can I write
>> now a == b? Nop... I need the loop you've just mentioned in all the
>> places where the comparison changes just in the operator, not in the
>> algorithm... (I would say this is bad coding practice...)
>
> In Lisp I think you'd use (equal (mapcar upcase a) (mapcar upcase
> b))

Or simply (equalp a b), since equalp comparisons happen to compare
strings case-insensitively. But that's Common Lisp... overflowing
kitchen sink.

> or something like that. In Python, a.upper() == b.upper().

I guess you meant map(a, str.upper) == map(b, str.upper)? a and b are
lists of strings.

Paul Rubin

unread,
Apr 25, 2009, 4:10:07 PM4/25/09
to
Hrvoje Niksic <hni...@xemacs.org> writes:
> I guess you meant map(a, str.upper) == map(b, str.upper)? a and b are
> lists of strings.

Oh, sorry. Yes, either

map(str.upper, a) == map(str.upper, b)

or

all(str.upper(x)==str.upper(y) for x,y in zip(a,b))

Carl Banks

unread,
Apr 25, 2009, 4:26:08 PM4/25/09
to
On Apr 25, 12:36 am, John Yeung <gallium.arsen...@gmail.com> wrote:
> On Apr 25, 2:06 am, Carl Banks <pavlovevide...@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).
>
> I don't get that impression from Lisp programmers.  I suppose it's
> only fair that I disclose (1) I admire Lisp, especially Scheme, (2) I
> hardly know Lisp at all, and (3) I don't frequent any Lisp forums/
> newsgroups/etc.

No it doesn't really apply to Scheme. (The Scheme programmer would
have stopped after implementing the new function.)

Some people seem to want to hypertarget everything. Common Lisp seems
to attract these people in big numbers because it has powerful
metaprogramming facilities and expressivity on steroids, making
hypertargeting easy. It's like the Ultimate Enabler language.


> I do get the impression that Lispers tend to feel Lisp is superior to
> all other languages, and I agree that in some ways it is.  I don't
> think most Lispers' main objection to Python is about "only one
> obvious way" but rather things like the limitations, compromises, and
> impurities in the language.

I totally disagree. Scheme might be a pure language with no
compromises and impurities, but Common Lisp is certainly not. The
"One Obvious Way" philosophy isn't their main objection so much as the
most emblematic difference.


> Certainly compared to Scheme, Python
> sacrifices a lot of purity for practicality.  (And I guess some fans
> of Scheme would argue that Common Lisp does the same!)
>
> Ultimately, Lisp is first and foremost academic (Scheme especially so)
> while Python is first and foremost practical.  I think Paul Graham's
> essays on Lisp exemplify the Lisp mentality.

I don't agree. I agree that Lisp programmers think that's their
mentality; I doubt many can actually take fullest advantage of Lisp
the way Graham has. I think Paul Graham is a freak of nature whose
brain is hardwired to notice patterns in places different from where
most peoeple see patterns. Graham, for his part, doesn't seem to
appreciate that what he does is beyond hope for average people, and
that sometimes reality requires average people to write programs.


Carl Banks

Carl Banks

unread,
Apr 25, 2009, 4:31:37 PM4/25/09
to
On Apr 25, 1:44 am, "Ciprian Dorin, Craciun"

<ciprian.crac...@gmail.com> wrote:
> On Sat, Apr 25, 2009 at 11:30 AM, "Martin v. Löwis" <mar...@v.loewis.de> wrote:
>
> >>>>     Ok... Then what's pythonic? Please give a pythonic implementation...
> >>> Use the builtin a==b, similar to (equal a b)
>
> >>     But how about extensibility?
> > [...]
>
> > I see that you allow for a different comparison function. I do wonder
> > what the use case for this is - in what application do you have to
> > compare two lists for equality, and the item's __eq__ is inappropriate?
> > What would break if you fix the item's __eq__, instead of writing
> > your own comparison algorithm?
>
> > [...]
>
>     A practical example: I have lists that contain strings, but I want
> to compare them in an case-insensitive way... Should I update the
> __eq__ method (for str class) and break almost everything?

The practical way to deal with this issue is to write your own
function when you encounter a situation where == doesn't suffice.


Carl Banks

Brett Hoerner

unread,
Apr 25, 2009, 4:36:49 PM4/25/09
to
On Apr 25, 8:11 am, "Ciprian Dorin, Craciun"

<ciprian.crac...@gmail.com> wrote:
>     Well in fact I would have written it like:
>
> def validate_commandline(rexes, line) :
>     if not compare (rexes, line, re.match) :
>         if len (rexes) != len (line) :
>             raise ValueError ("mismatch len")
>         mismatch = find_index (rexes, line, re.match, negate = True)
>         raise ValueError ("mismatch at %d" % (mismatch))
>
>     Assuming, that I would have the function find_index.
>
>     Ciprian.

I think you've hit on the definition of "unpythonic". (No, I don't
have a dictionary definition for you, sorry).

Using a function called "compare" to run a list of regexes against
another list of regexes to get a boolean? And then another find_index
function doing the same where you pass in negate? What is even going
on here?

I, for one, would take Martin's any day of the week. It reads like
good pseudocode as much "proper" Python does.

Brett

Ciprian Dorin, Craciun

unread,
Apr 25, 2009, 5:24:39 PM4/25/09
to Brett Hoerner, pytho...@python.org

From your comments I understand that the only problem with my code
proposal are the function names... Well instead of compare (which was
kept from the beginning of the post) we could just rename it to
"matches". Does the name "matches" matches what it does? :) (If not we
can keep discussing for a proper name...)

And about the find_index, we could rename it to
first_matching_index. About the negation optional parameter, we could
eliminate it if we allow either: to have another function
first_missmatching_index, but this leads to namespace bloat, or we
have a function named negate, that takes another function, and negates
it meaning. (Although i don't see anything wrong in the negate
argument... It's just like having order by asc | desc in SQL...)

Thus the code would have been rewritten as: (we also put the
function on the first argument as its the most important argument)

def validate_commandline(rexes, line) :
if not matches (re.match, rexes, line) :


if len (rexes) != len (line) :
raise ValueError ("mismatch len")

mismatch = first_matching_index (negate (re.match), rexes, line)


raise ValueError ("mismatch at %d" % (mismatch))

Ciprian.

Rhodri James

unread,
Apr 25, 2009, 8:08:02 PM4/25/09
to pytho...@python.org
On Sat, 25 Apr 2009 13:32:21 +0100, Ciprian Dorin, Craciun
<ciprian...@gmail.com> wrote:

>>> Should I update the


>>> __eq__ method (for str class) and break almost everything? Can I write
>>> now a == b?

Should you instead, perhaps, write a case-insensitive string class, since
seems to be what your data is asking for?

> I like object oriented programming, but most of the times we are
> just throwing together code and data even when the data has no
> behavior and the code is in fact just one possible processing
> algorithm. Like in the case you are mentioning, if I tie the
> comparison code to the actual data structure, then I'll never be able
> to reuse it... But if I leave the code as a standalone function, and
> just use the data (or any data that resembles the original structure)
> then maybe I'll be able to reuse it...

I'd say the reverse, personally. A lot of what you've demonstrated is
varying algorithms, which are rarely going to reuse standalone code.
They still need to deal with case-insensitive strings, though.

--
Rhodri James *-* Wildebeeste Herder to the Masses

Mark Wooding

unread,
Apr 25, 2009, 9:05:10 PM4/25/09
to
Carl Banks <pavlove...@gmail.com> writes:

> Graham, for his part, doesn't seem to appreciate that what he does is
> beyond hope for average people, and that sometimes reality requires
> average people to write programs.

I think he understands that perfectly well. But I think he believes
that the sorts of tools which help average people write programs get in
the way of true wizards.

I think I agree.

On the other hand, I don't think Python actually does get in the way
very much.

-- [mdw], Lisp hacker.

Carl Banks

unread,
Apr 25, 2009, 9:52:37 PM4/25/09
to
On Apr 25, 6:05 pm, Mark Wooding <m...@distorted.org.uk> wrote:

John Yeung

unread,
Apr 25, 2009, 11:06:55 PM4/25/09
to
On Apr 25, 9:05 pm, Mark Wooding <m...@distorted.org.uk> wrote:

Actually, Graham doesn't have particularly strong objection to
Python. Partly this is because he sees it as being largely as capable
and expressive as Lisp (mainly sans macros, of course); partly because
he sees that Python tends to attract good programmers (chief among
them Trevor Blackwell).

In my view, what is remarkable about Python is that it is so
accessible to average programmers (and frankly, even rather poor
programmers) while still managing to stay appealing to top-notch
programmers.

That said, my experience with Lisp programmers has mainly been with
people who like Scheme, which may explain why Carl Banks and I have
different impressions of Lisp programmers. (We also seem to differ on
how accurate it is to refer to Scheme as Lisp.) But in my experience,
Lisp in any form tends not to attract average programmers, and
certainly not poor programmers. I don't mean to say Banks is wrong; I
said up front my exposure to the Lisp community is limited. I am just
giving my own impressions.

Python is easily powerful enough and expressive enough to be an
"enabler language". I guess not Ultimate, but close enough that
Graham isn't particularly turned off by it!

John

namekuseijin

unread,
Apr 25, 2009, 11:26:51 PM4/25/09
to
Paul Rubin wrote:
> Carl Banks <pavlove...@gmail.com> writes:
>> Python programmer:
>>
>> a == b. Next question.
>
> in lisp you'd use (equal a b)

I see you walk both sides. :)

namekuseijin

unread,
Apr 25, 2009, 11:32:33 PM4/25/09
to
That was amusing, but that's not a question of Lisp vs Python
programmers, just one of fun vs practicality. Mark Tarver is the
implementor of Qi, a higher order Lisp of sorts. He's writing a
compiler from Qi to Python and was learning Python along the way.

He's having fun with it, not writing it to meet a deadline. Who never
reimplemented things for the sheer fun of it? The fun of writing it to
top the current implementation, to learn how it works or merely as
example to fellow programmers?

Oh, I know who never did it: programmers who are into programming only
for the paycheck and who otherwise think it's a bore.

Steven D'Aprano

unread,
Apr 26, 2009, 12:31:08 AM4/26/09
to
On Fri, 24 Apr 2009 23:06:30 -0700, Carl Banks wrote:


> 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.
[snip]


That's great stuff! An unfair and untrue caricature, but still great :)


--
Steven

Michele Simionato

unread,
Apr 26, 2009, 12:45:06 AM4/26/09
to
On Apr 25, 10:26 pm, Carl Banks <pavlovevide...@gmail.com> wrote:
> I totally disagree.  Scheme might be a pure language with no
> compromises and impurities, but Common Lisp is certainly not.

I can assure you that even Scheme is a language full
of compromises and inconsistencies :-/

Michele, who is now writing a book about Scheme

http://www.phyast.pitt.edu/~micheles/scheme/TheAdventuresofaPythonistainSchemeland.pdf

Steven D'Aprano

unread,
Apr 26, 2009, 12:54:49 AM4/26/09
to
On Sat, 25 Apr 2009 10:50:50 +0300, Ciprian Dorin, Craciun wrote:

> On Sat, Apr 25, 2009 at 10:43 AM, <bearoph...@lycos.com> wrote:
>> Ciprian Dorin, Craciun:
>>> 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
>>

>> That's not "pythonic".
>>
>> Bye,
>> bearophile
>> --
>> http://mail.python.org/mailman/listinfo/python-list


>
> Ok... Then what's pythonic? Please give a pythonic implementation...

Don't re-invent the wheel. Instead of creating your own functions, use
existing tools to your advantage.

import operator

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


But we can re-write that to be even more pythonic, by using the built-in
all():

def compare(a, b, comp=operator.eq):
    if len(a) != len(b):
        return False
return all(comp(x, y) for (x, y) in zip(a, b))

or even:

def compare(a, b, comp=operator.eq):
    return (len(a) == len(b)) and all(comp(*t) for t in zip(a, b))


(All the above are untested, so please excuse any errors.)


> Ciprian Craciun.
>
> P.S.: Also, I'm tired of hearing about the pythonic way... Where
> do I find a definitive description about the pythonic way?

There is no such thing as a definitive description of pythonic -- it is
like art, and pornography: you can recognise it when you see it (except
when you can't).

However, you can get close by doing:

import this

in the Python interactive interpreter. Or from a shell prompt:

python -m this


> I think that
> this word is used only when someone sees something that he doesn't like,
> he doesn't know what he doesn't like at it, and just goes to say its
> un-pythonic, without saying what would be... Wouldn't be just easier to
> say "I don't know" or "I doesn't feel right to me"?

I think that there's a risk that people over-use unpythonic when they
mean "I don't like it", but that doesn't mean that pythonic isn't a
meaningful concept. However, it is a matter of degree, not kind: like
mole-hills and mountains, unpythonic and pythonic are very different
things, but there's no precise dividing line between them.


--
Steven

Steven D'Aprano

unread,
Apr 26, 2009, 1:11:02 AM4/26/09
to
On Sat, 25 Apr 2009 10:30:56 +0200, Martin v. Löwis wrote:

>>>> Ok... Then what's pythonic? Please give a pythonic
>>>> implementation...

>>> Use the builtin a==b, similar to (equal a b)
>>
>> But how about extensibility?
>

> == is extensible. To compare two things for equality, use ==.
>
> This is what Carl Banks referred in his original posting. You just
> *don't* implement a function that compares two lists, not even as an
> exercise for estetic, optimal, and useful implementations - because
> you'll know that it won't be useful, anyway, if you can already use the
> builtin == in the first place.


>
> I see that you allow for a different comparison function. I do wonder
> what the use case for this is - in what application do you have to
> compare two lists for equality, and the item's __eq__ is inappropriate?

The above doesn't really compare for equality, it's a generic element-by-
element comparison function, and so it is inappropriate to contrast it to
__eq__ alone. Defaulting to equality testing is misleading, and if I were
writing such a function I'd remove the default.

compare(a, b, operator.eq) gives the same result as the simpler a == b,
but compare(a, b, operator.lt) does something very different to a < b. I
can't think of an application for element-by-element comparisons off the
top of my head, but if the numpy people use it, there must be a need :)


--
Steven

anonymous...@gmail.com

unread,
Apr 26, 2009, 1:35:04 AM4/26/09
to
On Apr 25, 2:06 am, Carl Banks <pavlovevide...@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.

(equal a b) or (equalp a b)

Next question??

I understand where you are going with the analogy, but I think a lot
of what you describe as 'over-thinking' of the problem in lisp comes
from people having an honest desire to answer the /right/ question.

Lisp gives you a bit of granularity with regard to certain things
(like comparison), that in many languages amount to a single '=='
operator. I /believe/ that this is because of its origins in symbolic
programming.

You don't compare numeric functions in Perl and C, and then say 'Oh
those silly C programmers over-thinking things, they must have 10
different types of numbers!'

namekuseijin

unread,
Apr 26, 2009, 1:59:54 AM4/26/09
to
Paul Rubin wrote:
> Python tries to be simple and pragmatic while not aiming for as
> heavy-duty applications as Common Lisp. Scheme is more of a research
> language that's way past its prime. If you like Scheme, you should
> try Haskell. Python has the motto "practicality beats purity".
> With Haskell, it's exactly the opposite ;-).

Going from Scheme to Haskell is about the same as going from Python to
Ruby: you get far more concise and obfuscated syntax and get boggled
down in tons of different ways to do the same thing.

Ciprian Dorin, Craciun

unread,
Apr 26, 2009, 1:52:30 AM4/26/09
to Steven D'Aprano, pytho...@python.org

I liked very much your implementation for the compare function, it
is very short and at the same time readable:

> def compare(a, b, comp=operator.eq):
> return (len(a) == len(b)) and all(comp(*t) for t in zip(a, b))

But I have only one problem, it is suboptimal, in the sense that:
* it constructs two intermediary lists (the list comprehension and
the zip call);
* it evaluates all the elements, even if one is false at the beginning;

So, could you overcome these problems?

About the pythonic vs unpythonic words, I agree with you, there
are ofter overused and misused...

Ciprian.

Ciprian Dorin, Craciun

unread,
Apr 26, 2009, 1:58:19 AM4/26/09
to Steven D'Aprano, pytho...@python.org
> --

I agree with your comment, the presented function compare does
more than this, in fact checks if the elements in the two functions
match a given binary predicate. (In Scheme this is named "andmap")

About the compare (a, b, operator.lt) it does the same as a < b,
where a and b are lists of numbers.

An usage for the compare function (as I've shown in a previous
post to this thread) could also have been checking a list of strings
if they match to a list of regular expressions (we rename the function
compare to the name "match" as it is much general):
match (re.match, regexps, strings)

About the defaults, I really, really, agree with your comment:
"Defaulting to equality testing is misleading"... Most of the times
equality means equality by value, but sometimes you need the equality
to be more relaxed (maybe ignore case, maybe ignore spaces, or maybe
for real numbers to ignore a difference smaller than a chosen delta
(those writing scientific applications know this too well))...

Ciprian.

namekuseijin

unread,
Apr 26, 2009, 2:26:40 AM4/26/09
to

This evaluates just until finding one that is false:

return (len(a) == len(b)) and not any(not comp(*t) for t in
(zip(a, b)))

plus the zip call enclosed in parentheses got turned into an iterator.

>>> def compare(a, b, comp=operator.eq):
... return (len(a) == len(b)) and not any(not comp(a,b) for (a,b) in
(zip(a, b)))
...
>>> compare( [1,2,3], [1,3,3] )
False
>>> compare( [1,2,3], [1,2,3] )
True

Steven D'Aprano

unread,
Apr 26, 2009, 2:56:38 AM4/26/09
to
On Sun, 26 Apr 2009 08:52:30 +0300, Ciprian Dorin, Craciun wrote:

> I liked very much your implementation for the compare function, it
> is very short and at the same time readable:
>
>> def compare(a, b, comp=operator.eq):
>> return (len(a) == len(b)) and all(comp(*t) for t in zip(a, b))
>
> But I have only one problem, it is suboptimal, in the sense that: *
> it constructs two intermediary lists (the list comprehension and
> the zip call);

If you look closely, there is no list comprehension. The argument to
all() is a generator expression, which does not construct an intermediary
list.

However, you are right that zip produces a list, at least in Python 2.x.
In Python 3 it produces a generator-like object.

> * it evaluates all the elements, even if one is false at the
> beginning;

No, all() uses short-cut evaluation. It will return as soon as it hits a
False element.

> So, could you overcome these problems?

In Python 2.4 or better, I can remove the intermediate list produced by
zip with one line:

from itertools import izip as zip

(This may even work in 2.3, but I don't have 2.3 to test it.)


--
Steven

Paul Rubin

unread,
Apr 26, 2009, 4:03:15 AM4/26/09
to
namekuseijin <namekusei...@gmail.com> writes:
> ... return (len(a) == len(b)) and not any(not comp(a,b) for (a,b)
> in (zip(a, b)))

If I'm reading that correctly, I think I'd write it as:

from itertools import imap, izip
return (len(a) == len(b)) and all(imap(comp, izip(a, b)))

That is more concise and avoids building an intermediate list with zip.

Maybe we need something like zipWith ...

Paul Rubin

unread,
Apr 26, 2009, 4:05:18 AM4/26/09
to
namekuseijin <namekusei...@gmail.com> writes:
> return (len(a) == len(b)) and not any(not comp(*t) for t in
> (zip(a, b)))
>
> plus the zip call enclosed in parentheses got turned into an iterator.

zip in python 2.x always makes a list. You want itertools.izip.
You could also use itertools.starmap.

Arnaud Delobelle

unread,
Apr 26, 2009, 4:05:25 AM4/26/09
to
namekuseijin <namekusei...@gmail.com> writes:

> Ciprian Dorin, Craciun wrote:
>> On Sun, Apr 26, 2009 at 7:54 AM, Steven D'Aprano
>> <st...@remove-this-cybersource.com.au> wrote:
>> I liked very much your implementation for the compare function, it
>> is very short and at the same time readable:
>>
>>> def compare(a, b, comp=operator.eq):
>>> return (len(a) == len(b)) and all(comp(*t) for t in zip(a, b))
>>
>> But I have only one problem, it is suboptimal, in the sense that:
>> * it constructs two intermediary lists (the list comprehension and
>> the zip call);

No, it only constructs one list (the zip() one) and only in Python 2.x -
in Python 3.x, zip return a special 'zip object'. There is no list
comprehension. It's a generator expression [1].

To avoid the list created by zip in python 2.x (for x large enough!),
just do:

from itertools import izip as zip

Another way to define the function that may appeal to you, as a lisper.

def compare(a, b, comp=operator.eq):
return len(a) == len(b) and all(map(comp, a, b))

The same remark applies to map() here as to zip() above.

>
>> * it evaluates all the elements, even if one is false at the beginning;

It does not [2].

> This evaluates just until finding one that is false:
>
> return (len(a) == len(b)) and not any(not comp(*t) for t in
> (zip(a, b)))
>
> plus the zip call enclosed in parentheses got turned into an iterator.

not any(not i for i in iterable) is not an optimisation of
all(iterable). Refer to [2]. Moreover, putting a list in parenthesis
does not magically turn it into a generator.

--
Arnaud

[1] http://www.python.org/dev/peps/pep-0289/
[2] http://docs.python.org/library/functions.html

Arnaud Delobelle

unread,
Apr 26, 2009, 4:14:55 AM4/26/09
to
Paul Rubin <http://phr...@NOSPAM.invalid> writes:

> namekuseijin <namekusei...@gmail.com> writes:
>> ... return (len(a) == len(b)) and not any(not comp(a,b) for (a,b)
>> in (zip(a, b)))
>
> If I'm reading that correctly, I think I'd write it as:
>
> from itertools import imap, izip
> return (len(a) == len(b)) and all(imap(comp, izip(a, b)))

Do you mean imap(comp, a, b)?

--
Arnaud

"Martin v. Löwis"

unread,
Apr 26, 2009, 5:08:36 AM4/26/09
to
> From your comments I understand that the only problem with my code
> proposal are the function names...

No, the problem is that you are using way too many functions, that do
too little. The problem with that is then that you have to give names
to all the functions, which then find people difficult to read because
they don't just need to the code in question itself; they also need to
dozen of helper functions that it relies on.

> And about the find_index, we could rename it to
> first_matching_index. About the negation optional parameter, we could
> eliminate it if we allow either: to have another function
> first_missmatching_index, but this leads to namespace bloat, or we
> have a function named negate, that takes another function, and negates
> it meaning.

Or you could avoid introducing the function altogether, to make it more
readable. This makes it more pythonic, also: readability counts (from
the Zen of Python).

Regards,
Martin

Paul Rubin

unread,
Apr 26, 2009, 5:16:03 AM4/26/09
to
Arnaud Delobelle <arn...@googlemail.com> writes:
> > return (len(a) == len(b)) and all(imap(comp, izip(a, b)))
> Do you mean imap(comp, a, b)?

Oh yes, I forgot you can do that. Thanks.

Ciprian Dorin, Craciun

unread,
Apr 26, 2009, 5:30:40 AM4/26/09
to Martin v. Löwis, pytho...@python.org


So if I'm reading right you are saying something in the lines:
"using too many functions is bad just because it is unreadable and
non-understandable to average (could I say mediocre?) programmers"...
Unfortunately I thought that delegating responsibilities to other
functions, and thus writing small chunks of code, is what good
software engineering is... Well my bad...

(As a side-note, maybe this is a reason why some of my students
find it hard to understand functional programming -- too many
functions that is -- I shall have to revise my teaching, and tell the
students to inline everything, maybe they'll understand it like this
:) )

Although you have a point -- that of being hard to comprehend by
average programmers -- but this doesn't mean it is a wrong (as in
ugly) solution... Also, with respects, but the "pythonic" solution
involving generators (or iterators) and "zip" or "all" function --
although I appreciate it as it comes close to FP -- is not what I
would call readable and understandable by non-guru programmers...

Ciprian Craciun.

Travis

unread,
Apr 26, 2009, 6:35:05 AM4/26/09
to

I've noticed that every one of you is wrong about programming.
Since I can't say it effectively, here's someone who can:
http://www.youtube.com/watch?v=XHosLhPEN3k

That's the answer.

bearoph...@lycos.com

unread,
Apr 26, 2009, 8:07:25 AM4/26/09
to
Paul Rubin:
> Arnaud Delobelle:

> > Do you mean imap(comp, a, b)?
>
> Oh yes, I forgot you can do that.  Thanks.

That works and is nice and readable:


import operator
from itertools import imap

def equal_sequences(a, b, comp=operator.eq):
"""
a and b must have __len__

>>> equal_sequences([1, 2, 3], [1, 2, 3])
True
>>> L1, L2 = [1, 2, 3], [1, -2, 3]
>>> equal_sequences(L1, L2)
False
>>> equal_sequences(L1, L2, lambda x,y: abs(x) == abs(y))
True
>>> L3, L4 = ["hello", "HALLO"], ["hello", "hallo"]
>>> equal_sequences(L3, L4)
False
>>> equal_sequences(L3, L4, lambda x,y: x.lower() == y.lower())
True
"""
return len(a) == len(b) and all(imap(comp, a, b))


if __name__ == "__main__":
import doctest
doctest.testmod()
print "Doctests finished.\n"

But both sequences must have a len. Otherwise you may use (if you
don't have izip_longest the folllowing code gets longer):


"""
>>> equal_items([1, 2], [1, 2, 3])
False
>>> equal_items([1, 2, 3], [1, 2, 3])
True
>>> equal_items([1, 2, 3], [1, -2, 3])
False
>>> equal_items([1, 2, 3], [1, -2, 3], abs)
True
>>> equal_items([1, 2, 3], [1, -2, 4], abs)
False
>>> L1, L2 = ["hello", "HALLO"], ["hello", "hallo"]
>>> equal_items(L1, L2)
False
>>> equal_items(L1, L2, str.lower)
True
>>> equal_items(xrange(3), (i for i in xrange(3)))
True
>>> equal_items([0, 1, 2], (i for i in xrange(3)))
True
>>> equal_items([0, 1, 2, 3], (i for i in xrange(3)))
False
>>> equal_items([-0, -1, -2], (i for i in xrange(3)), key=abs)
True
>>> equal_items([-0, -1, -2, -3], (i for i in xrange(3)), key=abs)
False
>>> x = []
>>> equal_items( (x for i in range(3)), (x for i in range(3)) )
True
>>> equal_items( (x for i in range(3)), (x for i in range(4)) )
False
>>> equal_items( (x for i in range(3)), (x for i in range(3)), key=id)
True
>>> equal_items( (x for i in range(3)), (x for i in range(4)), key=id)
False
>>> equal_items( (x for i in range(3)), (x for i in range(3)), key=lambda x:x)
True
>>> equal_items( (x for i in range(3)), (x for i in range(4)), key=lambda x:x)
False
"""

from itertools import izip_longest


def equal_items(iter1, iter2, key=None):
try:
len_iter1 = len(iter1)
len_iter2 = len(iter2)
except TypeError:
pass
else:
if len_iter1 != len_iter2:
return False

class Guard(object): pass

if key is None:
for x, y in izip_longest(iter1, iter2, fillvalue=Guard()):
if x != y:
return False
else:
try:
for x, y in izip_longest(iter1, iter2, fillvalue=Guard()):
if key(x) != key(y):
return False
except TypeError: # intercepts key(guard)
return False

return True


if __name__ == "__main__":
import doctest
doctest.testmod()
print "Doctests finished.\n"

You can write hairy code in Python too, not just in CLisp :-)


Bye,
bearophile

Tim Chase

unread,
Apr 26, 2009, 8:14:06 AM4/26/09
to Ciprian Dorin, Craciun, pytho...@python.org
> I liked very much your implementation for the compare function, it
> is very short and at the same time readable:
>
>> def compare(a, b, comp=operator.eq):
>> return (len(a) == len(b)) and all(comp(*t) for t in zip(a, b))
>
> But I have only one problem, it is suboptimal, in the sense that:
> * it constructs two intermediary lists (the list comprehension and
> the zip call);
> * it evaluates all the elements, even if one is false at the beginning;
>
> So, could you overcome these problems?

The solution would be to use itertools.izip() instead of the
stock zip(). The all() function short-circuits at the first
non-True value. Thus, using izip() instead will (1) not create
any new lists (it's a generator, not a list) and (2) the all()
will only look until it fails.

Just make sure that your comp() function returns equality for
this to work, or otherwise use "not comp()" (which is germane if
you use a __cmp__ function that returns 0 for equality)

-tkc


bearoph...@lycos.com

unread,
Apr 26, 2009, 8:20:04 AM4/26/09
to
You can also use quite less code, but this is less efficient:

def equal_items(iter1, iter2, key=lambda x: x):
class Guard(object): pass


try:
for x, y in izip_longest(iter1, iter2, fillvalue=Guard()):
if key(x) != key(y):
return False
except TypeError: # intercepts key(guard)
return False
return True

Bye,
bearophile

Vsevolod

unread,
Apr 26, 2009, 8:40:09 AM4/26/09
to

I think you're exaggerating. Go ask this question in c.l.l and the
first answer you'll get is mismatch.
But, from the other point of view your exaggeration makes sense: Lisp
unlike Python, IMO, is the language, where it's pleasant to program
not only applications, but the internals as well. So some people may
find interest in reprogramming what's already there. In lisp
programmer's mentality it's good to know, that you have that ability.

And let's look at my recent experience with Python: I wanted to
implement a daemon process and stumbled at a simplest problem with
threading: neither Thread, nor Threading module provides thread-
killing possibility. Surely, I'm not so experienced in Python as in
Lisp (in which I'd definitely be able to solve this problem by
extending the library), but I don't see an obvious solution, which
will stay inside the language: I have to either use the shell or stick
to the limited set of provided options and skew my program design to
work with them. Any other suggestions?

P.S. Btw the other issue with CL's mismatch is that it provides a
possibility to use any test and keyword extraction function.

Best regards,
Vsevolod Dyomkin

Arnaud Delobelle

unread,
Apr 26, 2009, 9:08:49 AM4/26/09
to
bearoph...@lycos.com writes:

You don't want to silence TypeErrors that may arise from with key() when
x or y is not a Guard, as it could hide bugs in key(). So I would write
something like this:

def equal_items(iter1, iter2, key=lambda x: x, _fill = object()):
for x, y in izip_longest(iter1, iter2, fillvalue=_fill):
if x is _fill or y is _fill or key(x) != key(y):
return False
return True

(untested)

Another way would be:

def equal_items(iter1, iter2, key=lambda x: x):

iter1, iter2 = iter(iter1), iter(iter2)
for x, y in izip(iter1, iter2):


if key(x) != key(y):
return False

for x, y in izip_longest(iter1, iter2):
return False
return True

(untested)

--
Arnaud

Arnaud Delobelle

unread,
Apr 26, 2009, 9:20:51 AM4/26/09
to
Arnaud Delobelle <arn...@googlemail.com> writes:

> Another way would be:
>
> def equal_items(iter1, iter2, key=lambda x: x):
> iter1, iter2 = iter(iter1), iter(iter2)
> for x, y in izip(iter1, iter2):
> if key(x) != key(y):
> return False
> for x, y in izip_longest(iter1, iter2):
> return False
> return True
>
> (untested)

Or even:

def equal_items(iter1, iter2, key=lambda x: x):
iter1, iter2 = iter(iter1), iter(iter2)
for x, y in izip(iter1, iter2):
if key(x) != key(y):
return False

return not any(izip_longest(iter1, iter2))

(untested)

Or even:

def equal_items(iter1, iter2, key=lambda x: x):
iter1, iter2 = iter(iter1), iter(iter2)

if any(key(x) != key(y) for x, y in izip(iter1, iter2)):
return False
return not any(izip_longest(iter1, iter2))

--
Arnaud

Peter Otten

unread,
Apr 26, 2009, 9:31:56 AM4/26/09
to
Arnaud Delobelle wrote:

> def equal_items(iter1, iter2, key=lambda x: x):
>     iter1, iter2 = iter(iter1), iter(iter2)
>     for x, y in izip(iter1, iter2):
>         if key(x) != key(y):
>             return False
>     for x, y in izip_longest(iter1, iter2):
>         return False
>     return True
>
> (untested)

This will fail when iter1 yields one more item than iter2. izip() then
consumes one extra item:

>>> from itertools import izip
>>> a = iter([1,2])
>>> list(izip(a, "b"))
[(1, 'b')]
>>> a.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

Peter

Piet van Oostrum

unread,
Apr 26, 2009, 9:37:39 AM4/26/09
to
>>>>> "Ciprian Dorin, Craciun" <ciprian...@gmail.com> (CDC) wrote:

>CDC> About the compare (a, b, operator.lt) it does the same as a < b,
>CDC> where a and b are lists of numbers.


>>> a=[1, 2, 3]
>>> b=[1, 2, 4]
>>> compare (a, b, operator.lt)
False
>>> a < b
True

--
Piet van Oostrum <pi...@cs.uu.nl>
URL: http://pietvanoostrum.com [PGP 8DAE142BE17999C4]
Private email: pi...@vanoostrum.org

bearoph...@lycos.com

unread,
Apr 26, 2009, 9:40:38 AM4/26/09
to
Arnaud Delobelle:

> You don't want to silence TypeErrors that may arise from with key() when
> x or y is not a Guard, as it could hide bugs in key(). So I would write
> something like this:
>
> def equal_items(iter1, iter2, key=lambda x: x, _fill = object()):
>     for x, y in izip_longest(iter1, iter2, fillvalue=_fill):
>         if x is _fill or y is _fill or key(x) != key(y):
>             return False
>     return True
>
> (untested)

You are right, thank you. Darn exceptions. You are often able to find
bugs in my code.

Here is a new version then (this is designed to be usable in practice,
that's why it's longer):


from itertools import izip_longest

def equal_iter(iter1, iter2, key=None):
"""
>>> equal_iter([1, 2], [1, 2, 3])
False
>>> equal_iter([1, 2, 3], [1, 2, 3])
True
>>> equal_iter([1, 2, 3], [1, -2, 3])
False
>>> equal_iter([1, 2, 3], [1, -2, 3], abs)
True
>>> equal_iter([1, 2, 3], [1, -2, 4], abs)


False
>>> L1, L2 = ["hello", "HALLO"], ["hello", "hallo"]

>>> equal_iter(L1, L2)
False
>>> equal_iter(L1, L2, str.lower)
True
>>> equal_iter(xrange(3), (i for i in xrange(3)))
True
>>> equal_iter([0, 1, 2], (i for i in xrange(3)))
True
>>> equal_iter([0, 1, 2, 3], (i for i in xrange(3)))
False
>>> equal_iter([-0, -1, -2], (i for i in xrange(3)), key=abs)
True
>>> equal_iter([-0, -1, -2, -3], (i for i in xrange(3)), key=abs)
False
>>> x = []
>>> equal_iter( (x for i in range(3)), (x for i in range(3)) )
True
>>> equal_iter( (x for i in range(3)), (x for i in range(4)) )
False
>>> equal_iter( (x for i in range(3)), (x for i in range(3)),
key=id)
True
>>> equal_iter( (x for i in range(3)), (x for i in range(4)),
key=id)
False
>>> equal_iter( (x for i in range(3)), (x for i in range(3)),
key=lambda x:x)
True
>>> equal_iter( (x for i in range(3)), (x for i in range(4)),
key=lambda x:x)
False
>>> # bug found by Arnaud Delobelle
>>> def k(x): raise TypeError
>>> equal_iter( (x for i in range(3)), (x for i in range(3)),
key=k)


Traceback (most recent call last):

...
TypeError


"""
try:
len_iter1 = len(iter1)
len_iter2 = len(iter2)
except TypeError:
pass
else:
if len_iter1 != len_iter2:
return False

class Guard(object): pass
guard = Guard()

if key is None:
for x, y in izip_longest(iter1, iter2, fillvalue=guard):


if x != y:
return False
else:

for x, y in izip_longest(iter1, iter2, fillvalue=guard):
if x is guard or y is guard or key(x) != key(y):
return False

return True


if __name__ == "__main__":
import doctest
doctest.testmod()
print "Doctests finished.\n"

Bye,
bearophile

Arnaud Delobelle

unread,
Apr 26, 2009, 10:37:17 AM4/26/09
to
Peter Otten <__pet...@web.de> writes:

> Arnaud Delobelle wrote:
>
>> def equal_items(iter1, iter2, key=lambda x: x):
>>     iter1, iter2 = iter(iter1), iter(iter2)
>>     for x, y in izip(iter1, iter2):
>>         if key(x) != key(y):
>>             return False
>>     for x, y in izip_longest(iter1, iter2):
>>         return False
>>     return True
>>
>> (untested)
>
> This will fail when iter1 yields one more item than iter2. izip() then
> consumes one extra item:

Ah yes! And this is why I should have tested it.

--
Arnaud

Aahz

unread,
Apr 26, 2009, 11:28:02 AM4/26/09
to
In article <a2c0da95-1f52-46f6...@z23g2000prd.googlegroups.com>,

Vsevolod <vsel...@gmail.com> wrote:
>
>And let's look at my recent experience with Python: I wanted to
>implement a daemon process and stumbled at a simplest problem with
>threading: neither Thread, nor Threading module provides thread-
>killing possibility. Surely, I'm not so experienced in Python as in
>Lisp (in which I'd definitely be able to solve this problem by
>extending the library), but I don't see an obvious solution, which
>will stay inside the language: I have to either use the shell or stick
>to the limited set of provided options and skew my program design to
>work with them. Any other suggestions?

The problem is that thread-killing (in the literal sense) doesn't work.
Unlike processes, there's no thread-environment encapsulation at the OS
level, which means that things don't get cleaned up properly. Even Java
has mostly given up on thread-killing. The only way to kill threads
safely is to have them terminate themselves. Your other option is to use
multiple processes.
--
Aahz (aa...@pythoncraft.com) <*> http://www.pythoncraft.com/

"If you think it's expensive to hire a professional to do the job, wait
until you hire an amateur." --Red Adair

Scott David Daniels

unread,
Apr 26, 2009, 11:52:28 AM4/26/09
to
Travis wrote:
> ... I've noticed that every one of you is wrong about programming.

> Since I can't say it effectively, here's someone who can:
> http://www.youtube.com/watch?v=XHosLhPEN3k
>
> That's the answer.

That is a wonderful link. Thanks for sharing.

--Scott David Daniels
Scott....@Acm.Org

Aaron Brady

unread,
Apr 26, 2009, 11:53:44 AM4/26/09
to
On Apr 26, 10:52 am, Scott David Daniels <Scott.Dani...@Acm.Org>
wrote:
> Scott.Dani...@Acm.Org

import mentality? help( mentality ).

Mikael Jansson

unread,
Apr 26, 2009, 12:34:29 PM4/26/09
to
On Apr 25, 9:36 am, John Yeung <gallium.arsen...@gmail.com> wrote:
>
> Ultimately, Lisp is first and foremost academic (Scheme especially so)
> while Python is first and foremost practical.  I think Paul Graham's
> essays on Lisp exemplify the Lisp mentality.
>

Oh, you mean Blub?

http://www.paulgraham.com/avg.html

;-)

--
Mikael Jansson
http://mikael.jansson.be

"Martin v. Löwis"

unread,
Apr 26, 2009, 1:53:26 PM4/26/09
to pytho...@python.org
> So if I'm reading right you are saying something in the lines:
> "using too many functions is bad just because it is unreadable and
> non-understandable to average (could I say mediocre?) programmers"...

No, this style is also unreadable to advanced programmers, in
particular when you fail to include the functions you use, and
leave the reader with guesswork.

> Unfortunately I thought that delegating responsibilities to other
> functions, and thus writing small chunks of code, is what good
> software engineering is... Well my bad...

You misunderstood indeed. Splitting a large algorithm into smaller
parts is good if the original algorithm cannot be easily understood
in a single piece. By splitting it, you introduce new abstractions
that can help to simplify the original algorithm. OTOH, adding
these abstractions also requires the reader to familiarize himself
with the abstractions. There is a tradeoff, and the optimum is
somewhere in the middle.

Splitting code into tiny functions certainly is *not* good software
engineering.

> Although you have a point -- that of being hard to comprehend by
> average programmers -- but this doesn't mean it is a wrong (as in
> ugly) solution... Also, with respects, but the "pythonic" solution
> involving generators (or iterators) and "zip" or "all" function --
> although I appreciate it as it comes close to FP -- is not what I
> would call readable and understandable by non-guru programmers...

Please go back to my formulation - it doesn't use any of this, and
I agree that the formulations with zip, generators, etc are indeed
more difficult to read (perhaps *precisely* because they come
closer to FP).

Regards,
Martin

namekuseijin

unread,
Apr 26, 2009, 2:07:42 PM4/26/09
to

hmm, somehow I thought putting any sequence into parenthesis would
always yield a generator...

Scott David Daniels

unread,
Apr 26, 2009, 2:05:48 PM4/26/09
to
Ciprian Dorin, Craciun wrote:
> On Sun, Apr 26, 2009 at 12:08 PM, "Martin v. Löwis" <mar...@v.loewis.de> wrote:
>>> From your comments I understand that the only problem with my code
>>> proposal are the function names...
>> No, the problem is that you are using way too many functions, that do
>> too little. The problem with that is then that you have to give names
>> to all the functions, which then find people difficult to read because
>> they don't just need to the code in question itself; they also need to
>> dozen of helper functions that it relies on.
>>> And about the find_index, we could rename it to
>>> first_matching_index. About the negation optional parameter, we could
>>> eliminate it if we allow either: to have another function
>>> first_missmatching_index, but this leads to namespace bloat, or we
>>> have a function named negate, that takes another function, and negates
>>> it meaning.
>> Or you could avoid introducing the function altogether, to make it more
>> readable. This makes it more pythonic, also: readability counts (from
>> the Zen of Python).
>
> So if I'm reading right you are saying something in the lines:
> "using too many functions is bad just because it is unreadable and
> non-understandable to average (could I say mediocre?) programmers"...
> Unfortunately I thought that delegating responsibilities to other
> functions, and thus writing small chunks of code, is what good
> software engineering is... Well my bad...
> (As a side-note, maybe this is a reason why some of my students
> find it hard to understand functional programming -- too many
> functions that is -- I shall have to revise my teaching, and tell the
> students to inline everything, maybe they'll understand it like this
> :) )
> Although you have a point -- that of being hard to comprehend by
> average programmers -- but this doesn't mean it is a wrong (as in
> ugly) solution... Also, with respects, but the "pythonic" solution
> involving generators (or iterators) and "zip" or "all" function --
> although I appreciate it as it comes close to FP -- is not what I
> would call readable and understandable by non-guru programmers...

Writing software is writing for a reader: the maintainer or extender
of the code you are writing. That that person may be you in three
years is incidental. You should struggle to write clearly, and take
any clues you can get about how to make the code clearer. This doesn't
mean you must explain the language; you may assume the reader knows
(or can quickly find in manuals) what any particular language feature
means. What you must explain is how you are solving the problem at
every point in the code where the code itself does not make that clear.

I don't remember who, but something famously said, in effect:
Debugging is hard, maybe twice as hard as writing the code in
the first place. Unless you are one of those nonexistent few
who always write correct programs from the word go, you will
have to debug your own code before it works fully correctly.
Therefore, you had better write code so simple that you can
know what is going wrong with it. If your code is too hard
to understand for the average programmer, you are either four
times as brilliant as those "average" programmers or you are
in big trouble.

--Scott David Daniels
Scott....@Acm.Org

namekuseijin

unread,
Apr 26, 2009, 2:12:47 PM4/26/09
to
Travis wrote:
> I've noticed that every one of you is wrong about programming.
> Since I can't say it effectively, here's someone who can:
> http://www.youtube.com/watch?v=XHosLhPEN3k
>
> That's the answer.

Hmm, perhaps it was the answer by the time that song was written? ;)

cool anyway... :)

Vsevolod

unread,
Apr 26, 2009, 2:23:13 PM4/26/09
to
On Apr 26, 6:28 pm, a...@pythoncraft.com (Aahz) wrote:
> The problem is that thread-killing (in the literal sense) doesn't work.
> Unlike processes, there's no thread-environment encapsulation at the OS
> level, which means that things don't get cleaned up properly. Even Java
> has mostly given up on thread-killing. The only way to kill threads
> safely is to have them terminate themselves. Your other option is to use
> multiple processes.
Well, somehow, in Lisp it's not a problem. :)

Cheers,
Vsevolod

Aahz

unread,
Apr 26, 2009, 2:23:53 PM4/26/09
to
In article <xsadnQqpYqL-PGnU...@pdx.net>,

Scott David Daniels <Scott....@Acm.Org> wrote:
>
>I don't remember who, but something famously said, in effect:
> Debugging is hard, maybe twice as hard as writing the code in
> the first place. Unless you are one of those nonexistent few
> who always write correct programs from the word go, you will
> have to debug your own code before it works fully correctly.
> Therefore, you had better write code so simple that you can
> know what is going wrong with it. If your code is too hard
> to understand for the average programmer, you are either four
> times as brilliant as those "average" programmers or you are
> in big trouble.

Fom my .sig database:

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are, by
definition, not smart enough to debug it." --Brian W. Kernighan

Carl Banks

unread,
Apr 26, 2009, 5:24:13 PM4/26/09
to
On Apr 26, 5:40 am, Vsevolod <vselo...@gmail.com> wrote:
> On Apr 25, 9:06 am, Carl Banks <pavlovevide...@gmail.com> wrote:
> > Carl Banks, who might be exaggerating
>
> > ...a little.
>
> I think you're exaggerating. Go ask this question in c.l.l and the
> first answer you'll get is mismatch.

What could have made you think I was exaggerating. Could it be the
line where I said "Carl, Banks who mighe exaggerating"?? :)


> But, from the other point of view your exaggeration makes sense: Lisp
> unlike Python, IMO, is the language, where it's pleasant to program
> not only applications, but the internals as well. So some people may
> find interest in reprogramming what's already there. In lisp
> programmer's mentality it's good to know, that you have that ability.

And yet, the impression I get from Lisp programmers is that they
prefer to program internals. They will say it's an advantage of Lisp
that their list type is handled with low-level primitives like car,
cdr, and cons, rather than high-level operations like indexing,
appending, etc. Why would that be an advantage unless they are doing
that kind of stuff all the time?


> And let's look at my recent experience with Python: I wanted to
> implement a daemon process and stumbled at a simplest problem with
> threading: neither Thread, nor Threading module provides thread-
> killing possibility. Surely, I'm not so experienced in Python as in
> Lisp (in which I'd definitely be able to solve this problem by
> extending the library), but I don't see an obvious solution, which
> will stay inside the language: I have to either use the shell or stick
> to the limited set of provided options and skew my program design to
> work with them. Any other suggestions?

Say you are running a thread and you want the power to be able to kill
it at any time. The thread is either communicating with the rest of
the program periodically, or it isn't. If it is, then there are ample
opportunities to tell the thread to terminate itself. If it isn't,
then you might as well use a separate process which you can kill.
Next question.


Carl Banks

Paul Rubin

unread,
Apr 26, 2009, 5:38:16 PM4/26/09
to
Carl Banks <pavlove...@gmail.com> writes:
> Say you are running a thread and you want the power to be able to kill
> it at any time. The thread is either communicating with the rest of
> the program periodically, or it isn't. If it is, then there are ample
> opportunities to tell the thread to terminate itself. If it isn't,
> then you might as well use a separate process which you can kill.

That's not so satisfying. If you use a separate process, it can't
share Python objects with the main process, isn't under the same
memory management, etc. With the standard modules that comes with
Python, you can't share memory at all (except with mmap, which gives
no synchronization mechanisms). You can't pass open sockets from one
process to another with the standard library, making it harder to
implement typical multi-threaded servers. You do get better
scalability, but at the expense of having to serialize all IPC data
and use heavyweight communication mechanisms. Threads exist because
they are useful.

Carl Banks

unread,
Apr 26, 2009, 5:55:54 PM4/26/09
to
On Apr 26, 2:38 pm, Paul Rubin <http://phr...@NOSPAM.invalid> wrote:

> Carl Banks <pavlovevide...@gmail.com> writes:
> > Say you are running a thread and you want the power to be able to kill
> > it at any time.  The thread is either communicating with the rest of
> > the program periodically, or it isn't.  If it is, then there are ample
> > opportunities to tell the thread to terminate itself.  If it isn't,
> > then you might as well use a separate process which you can kill.
>
> That's not so satisfying.  If you use a separate process, it can't
> share Python objects with the main process,

Which is "communicating with the rest of the program periodically".

Presumably you have to protect objects to share them? There you go:
anytime you try to acquire a lock have the thread check to see whether
to abort.


Carl Banks

Scott David Daniels

unread,
Apr 26, 2009, 6:05:06 PM4/26/09
to
Paul Rubin wrote:
> Carl Banks <pavlove...@gmail.com> writes:
>> Say you are running a thread and you want the power to be able to kill
>> it at any time. The thread is either communicating with the rest of
>> the program periodically, or it isn't. If it is, then there are ample
>> opportunities to tell the thread to terminate itself. If it isn't,
>> then you might as well use a separate process which you can kill.
>
> That's not so satisfying. If you use a separate process, it can't
> share Python objects with the main process, isn't under the same
> memory management, etc.
But precisely because of that sharing the thread may be in the middle
something that "must complete" -- no with-statement locking will get
unlocked, no "finally:" clauses in code in the standard library, no
... -- there is just too much that goes wrong when a thread is
infinitely starved (which, in effect, is what would happen if you
could kill it).

--Scott David Daniels
Scott....@Acm.Org

Paul Rubin

unread,
Apr 26, 2009, 6:03:48 PM4/26/09
to
Carl Banks <pavlove...@gmail.com> writes:
> Which is "communicating with the rest of the program periodically".
>
> Presumably you have to protect objects to share them? There you go:
> anytime you try to acquire a lock have the thread check to see whether
> to abort.

Normally, acquiring a lock doesn't require running code in other
threads, at least in the uncontended case. If you have to switch
threads twice in order to acquire a lock, your implementation could
use some improvement.

Paul Rubin

unread,
Apr 26, 2009, 6:07:05 PM4/26/09
to
Scott David Daniels <Scott....@Acm.Org> writes:
> But precisely because of that sharing the thread may be in the middle
> something that "must complete" -- no with-statement locking will get
> unlocked, no "finally:" clauses in code in the standard library, no
> ... -- there is just too much that goes wrong when a thread is
> infinitely starved (which, in effect, is what would happen if you
> could kill it).

Right, it's better to recognize this sort of problem than to brush it
off. There was a discussion in sourceforge a long time ago about
adding a way to raise exceptions in threads, and I think there is some
way to do it through the C API.

Dan Sommers

unread,
Apr 26, 2009, 7:14:35 PM4/26/09
to pytho...@python.org
On Sun, 26 Apr 2009 12:30:40 +0300, Ciprian Dorin, Craciun wrote:

> On Sun, Apr 26, 2009 at 12:08 PM, "Martin v. Löwis" <mar...@v.loewis.de>
> wrote:

>> No, the problem is that you are using way too many functions, that do
>> too little. The problem with that is then that you have to give names
>> to all the functions, which then find people difficult to read because
>> they don't just need to the code in question itself; they also need to
>> dozen of helper functions that it relies on.

>> Or you could avoid introducing the function altogether, to make it more


>> readable. This makes it more pythonic, also: readability counts (from
>> the Zen of Python).

> So if I'm reading right you are saying something in the lines:
> "using too many functions is bad just because it is unreadable and
> non-understandable to average (could I say mediocre?) programmers"...
> Unfortunately I thought that delegating responsibilities to other
> functions, and thus writing small chunks of code, is what good software
> engineering is... Well my bad...

Also from the Zen: flat is better than nested. One of the aspects of
flatter call trees and object hierarchies is that I hit the bottom
(language features or the standard library) sooner, and I "should"
already have the language and its standard library in my brain. That
said, I also tend to break my programs into layers, but I do try to make
each layer as thin as possible (but no thinner).

> Although you have a point -- that of being hard to comprehend by
> average programmers -- but this doesn't mean it is a wrong (as in ugly)
> solution... Also, with respects, but the "pythonic" solution involving
> generators (or iterators) and "zip" or "all" function -- although I
> appreciate it as it comes close to FP -- is not what I would call
> readable and understandable by non-guru programmers...

I finally got it through my thick head that I've been *doing* functional
programming with [*nix] shells and pipes for years, well back into my non-
guru days.

Dan

--
Dan Sommers A death spiral goes clock-
<http://www.tombstonezero.net/dan/> wise north of the equator.
Atoms are not things. -- Werner Heisenberg -- Dilbert's PHB

Steven D'Aprano

unread,
Apr 26, 2009, 7:23:47 PM4/26/09
to
On Sun, 26 Apr 2009 05:07:25 -0700, bearophileHUGS wrote:

> Paul Rubin:
>> Arnaud Delobelle:
>> > Do you mean imap(comp, a, b)?
>>
>> Oh yes, I forgot you can do that.  Thanks.
>
> That works and is nice and readable:
>
>
> import operator
> from itertools import imap
>
> def equal_sequences(a, b, comp=operator.eq):
[snip]

Sorry, this is worse than unreadable. It is *misleading*. It doesn't test
for equal sequences except as a special case. What it does is perform a
generic element-by-element comparison, not necessarily an equality test.

What was wrong with the name compare() used in previous posts in this
thread?

--
Steven

Aahz

unread,
Apr 26, 2009, 8:20:58 PM4/26/09
to
In article <793a5176-ec2d-4ffd...@v35g2000pro.googlegroups.com>,

Does Lisp even have OS-level threads? What Lisp are you using, on what
OS? Are they perhaps Erlang-style cooperative threads instead?

Carl Banks

unread,
Apr 26, 2009, 8:59:01 PM4/26/09
to
On Apr 26, 3:03 pm, Paul Rubin <http://phr...@NOSPAM.invalid> wrote:

Come on, you're just making stuff up. How the *hell* do you get
switching threads twice out of that? I'm saying that if threads have
to synchronize data, then it's no big deal to have the thread check a
flag over whether to terminate when synchronizing. Take a look at
this recipe for an example:

http://code.activestate.com/recipes/576461/

That concept, I say, can be applied to any thread that shares data
with the main thread, with any sort of regularity, even if it's
doesn't use queues specifically.

The only time it's any trouble to notify a thread to terminate is when
that thread's running code that's not aware it's in a threaded app
(which implies that it's not sharing any data, aside from read-only).
But since it's not communicating with another threads you might as
well run it in another process; communication overhead is not going to
be a big deal in that case.


Carl banks

Ciprian Dorin, Craciun

unread,
Apr 27, 2009, 12:57:00 AM4/27/09
to Dan Sommers, pytho...@python.org
On Mon, Apr 27, 2009 at 2:14 AM, Dan Sommers <somm...@bellsouth.net> wrote:
> On Sun, 26 Apr 2009 12:30:40 +0300, Ciprian Dorin, Craciun wrote:
>
>> On Sun, Apr 26, 2009 at 12:08 PM, "Martin v. Löwis" <mar...@v.loewis.de>
>> wrote:
>
>>> No, the problem is that you are using way too many functions, that do
>>> too little. The problem with that is then that you have to give names
>>> to all the functions, which then find people difficult to read because
>>> they don't just need to the code in question itself; they also need to
>>> dozen of helper functions that it relies on.
>
>>> Or you could avoid introducing the function altogether, to make it more
>>> readable. This makes it more pythonic, also: readability counts (from
>>> the Zen of Python).
>
>>     So if I'm reading right you are saying something in the lines:
>> "using too many functions is bad just because it is unreadable and
>> non-understandable to average (could I say mediocre?) programmers"...
>> Unfortunately I thought that delegating responsibilities to other
>> functions, and thus writing small chunks of code, is what good software
>> engineering is... Well my bad...
>
> Also from the Zen:  flat is better than nested.  One of the aspects of
> flatter call trees and object hierarchies is that I hit the bottom
> (language features or the standard library) sooner, and I "should"
> already have the language and its standard library in my brain.  That
> said, I also tend to break my programs into layers, but I do try to make
> each layer as thin as possible (but no thinner).


I agree with your opinion about keeping the abstraction layers
shallow, but in my view high-order and helper functions do not
comprise a new abstraction layer. For example in Lisp, using map,
reduce (fold), or any other high-order function is just like using
for, or while in a normal imperative language.

Another example is the simple Observer pattern. How many think of
it as a new layer? Instead anytime we see a method that is named
add_listener we already have an idea of what it does... So we could
say that in FP world there are some patterns that involve "delegating
control" to them.

And also I would like to point out that "hitting the language
sooner", means to know every function in the standard Python library
(which is by far uncomprehensible, its huge) and most of the times you
also need the documentation. And if we go this path, when debugging we
could use a smart IDE, which should show as tool-tip-text for function
names their documentation, and in this way all we have to do to
understand a particular function is just to point it out.

Ciprian.

Vsevolod

unread,
Apr 27, 2009, 5:55:18 AM4/27/09
to
On Apr 27, 3:20 am, a...@pythoncraft.com (Aahz) wrote:
> In article <793a5176-ec2d-4ffd-b1e7-762077733...@v35g2000pro.googlegroups.com>,

>
> Vsevolod <vselo...@gmail.com> wrote:
> >On Apr 26, 6:28 pm, a...@pythoncraft.com (Aahz) wrote:
>
> >> The problem is that thread-killing (in the literal sense) doesn't work.
> >> Unlike processes, there's no thread-environment encapsulation at the OS
> >> level, which means that things don't get cleaned up properly. Even Java
> >> has mostly given up on thread-killing. The only way to kill threads
> >> safely is to have them terminate themselves. Your other option is to use
> >> multiple processes.
>
> >Well, somehow, in Lisp it's not a problem. :)
>
> Does Lisp even have OS-level threads? What Lisp are you using, on what
> OS? Are they perhaps Erlang-style cooperative threads instead?
> --
> Aahz (a...@pythoncraft.com) <*> http://www.pythoncraft.com/

>
> "If you think it's expensive to hire a professional to do the job, wait
> until you hire an amateur." --Red Adair

Different Lisp implementations provide different solutions. SBCL
provides OS-level threads (on Linux), which I personally use, while
CMUCL offers green threads. Allegro, LispWorks, Clozure CL, Sceineer
CL and ECL as well have threading, but I don't use them, so won't
speak, which implementation of threading they have. There's a common
unification library -- bordeaux-threads -- that abstracts away
implementation specifics. It's API includes the function destroy-
thread.

As well I'd like to outline, that, IMO, your answer exhibits the
common attitude among pythonistas: everything should be done in one
true way, which is the best option (and that is how it's implemented
in the current version of the language). As of PEP-20: "There should
be one-- and preferably only one --obvious way to do it. Although that
way may not be obvious at first unless you're Dutch." And if someone
disagrees -- he just doesn't understand...

Cheers,
Vsevolod

Richard Brodie

unread,
Apr 27, 2009, 7:17:11 AM4/27/09
to

"Vsevolod" <vsel...@gmail.com> wrote in message
news:42cebb2b-0361-416c...@y6g2000prf.googlegroups.com...

> There's a common unification library -- bordeaux-threads --
> that abstracts away implementation specifics. It's API includes
> the function destroy-thread.

Which is deprecated, like the Java one. It's not hard to provide
a kill thread call, if you don't mind it having undefined semantics.


Vsevolod

unread,
Apr 27, 2009, 7:27:09 AM4/27/09
to
On Apr 27, 2:17 pm, "Richard Brodie" <R.Bro...@rl.ac.uk> wrote:
> "Vsevolod" <vselo...@gmail.com> wrote in message

"This should be used with caution: it is implementation-defined
whether the thread runs cleanup forms or releases its locks first."
This doesn't mean deprecated. It means: implementation-dependent. For
example in SBCL: "Terminate the thread identified by thread, by
causing it to run sb-ext:quit - the usual cleanup forms will be
evaluated". And it works fine.

Best regards,
Vsevolod

Marco Mariani

unread,
Apr 27, 2009, 9:15:42 AM4/27/09
to
Scott David Daniels wrote:

> I don't remember who, but something famously said, in effect:
> Debugging is hard, maybe twice as hard as writing the code in
> the first place. Unless you are one of those nonexistent few

He would be the K in K&R.

Paul Rubin

unread,
Apr 27, 2009, 11:43:09 AM4/27/09
to
Carl Banks <pavlove...@gmail.com> writes:
> > > Presumably you have to protect objects to share them? �There you go:
> > > anytime you try to acquire a lock have the thread check to see whether
> > > to abort.
> >
> > Normally, acquiring a lock doesn't require running code in other
> > threads, at least in the uncontended case. �If you have to switch
> > threads twice in order to acquire a lock, your implementation could
> > use some improvement.
>
> Come on, you're just making stuff up. How the *hell* do you get
> switching threads twice out of that?

I think I mis-read your suggestion. I thought you meant to query
another thread before acquiring a lock.

Anyway, the usual reason to want to kill a thread is that it's become
unresponsive for some reason, while holding onto some resource like a
lock or a bound socket. This would be in the category of fault
recovery from conditions that weren't expected ahead of time.

Another situation is when you want to just stop the program
altogether, similar to kill -9'ing it, with no attempt to shut down
gracefully. Kind of a global ctrl-C. Right now there is no way to do
that for a multithreaded program except by literally sending a process
kill, which is pretty ugly.

Zamnedix

unread,
Apr 27, 2009, 12:16:33 PM4/27/09
to
On Apr 24, 11:06 pm, Carl Banks <pavlovevide...@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.

Hahaha. I love it.

Aahz

unread,
Apr 27, 2009, 12:16:37 PM4/27/09
to
In article <42cebb2b-0361-416c...@y6g2000prf.googlegroups.com>,

Vsevolod <vsel...@gmail.com> wrote:
>
>As well I'd like to outline, that, IMO, your answer exhibits the
>common attitude among pythonistas: everything should be done in one
>true way, which is the best option (and that is how it's implemented
>in the current version of the language).

Did you see my comment about Java? This particular issue has little to
do with Python. I won't disagree that what you're describing is
sometimes a problem in the Python community, but you're picking the
wrong issue to claim its relevance.
--
Aahz (aa...@pythoncraft.com) <*> http://www.pythoncraft.com/

Vsevolod

unread,
Apr 27, 2009, 1:08:29 PM4/27/09
to
On Apr 27, 7:16 pm, a...@pythoncraft.com (Aahz) wrote:
> Did you see my comment about Java? This particular issue has little to
> do with Python. I won't disagree that what you're describing is
> sometimes a problem in the Python community, but you're picking the
> wrong issue to claim its relevance.

OK, I'm not a big expert in Python. That was just the thing, that was
important to me recently. I won't claim, that it's regularly a problem
with Python, although from reading Guido's blog I get that impression.
(Well, I understand, that's how BDFLs should behave :)
Yet there was no response to my point, that the original example was
not realistically depicting the Lisp world, while more characteristic
of the Python one.

Best regards,
Vsevolod

Aahz

unread,
Apr 27, 2009, 1:18:16 PM4/27/09
to
In article <22272831-8d11-42d6...@p6g2000pre.googlegroups.com>,

Vsevolod <vsel...@gmail.com> wrote:
>
>Yet there was no response to my point, that the original example was
>not realistically depicting the Lisp world, while more characteristic
>of the Python one.

That's because there's no response to make; the original post was a joke,
and trying to have a serious discussion about it rarely excites people.

If you want to talk about Python and problems you're running into, you
should start a new thread.

David Bolen

unread,
Apr 27, 2009, 4:31:09 PM4/27/09
to
Vsevolod <vsel...@gmail.com> writes:

> "This should be used with caution: it is implementation-defined
> whether the thread runs cleanup forms or releases its locks first."
> This doesn't mean deprecated. It means: implementation-dependent. For
> example in SBCL: "Terminate the thread identified by thread, by
> causing it to run sb-ext:quit - the usual cleanup forms will be
> evaluated". And it works fine.

I'm curious - do you know what happens if threading is implemented as
a native OS thread and it's stuck in an I/O operation that is blocked?
How does the Lisp interpreter/runtime gain control again in order to
execute the specified function? I guess on many POSIX-ish
environments, internally generating a SIGALRM to interrupt a system
operation might work, but it would likely have portability problems.

Or is that combination (native OS thread and/or externally blocking
I/O) prevented by the runtime somehow (perhaps by internally polling
what appears to code as blocking I/O)? But surely if there's an
access to OS routines, the risk of blocking must be present?

That scenario is really the only rationale use case I've run into for
wanting to kill a thread, since in other cases the thread can be
monitoring for an application defined way to shut down.

-- David

Paul Rubin

unread,
Apr 27, 2009, 5:37:43 PM4/27/09
to
David Bolen <db3l...@gmail.com> writes:
> I'm curious - do you know what happens if threading is implemented as
> a native OS thread and it's stuck in an I/O operation that is blocked?
> How does the Lisp interpreter/runtime gain control again in order to
> execute the specified function? I guess on many POSIX-ish
> environments, internally generating a SIGALRM to interrupt a system
> operation might work, but it would likely have portability problems.

Someone wrote a paper proposing some Python language extensions to
support asynchronous exceptions in Python threads. I don't have the
url handy but you can probably search for it.

Dan Sommers

unread,
Apr 27, 2009, 10:26:08 PM4/27/09
to pytho...@python.org
On Mon, 27 Apr 2009 07:57:00 +0300, Ciprian Dorin, Craciun wrote:

> On Mon, Apr 27, 2009 at 2:14 AM, Dan Sommers <somm...@bellsouth.net>
> wrote:

>> Also from the Zen:  flat is better than nested.  One of the aspects of
>> flatter call trees and object hierarchies is that I hit the bottom
>> (language features or the standard library) sooner, and I "should"
>> already have the language and its standard library in my brain.  That
>> said, I also tend to break my programs into layers, but I do try to
>> make each layer as thin as possible (but no thinner).
>
> I agree with your opinion about keeping the abstraction layers
> shallow, but in my view high-order and helper functions do not comprise
> a new abstraction layer. For example in Lisp, using map, reduce (fold),
> or any other high-order function is just like using for, or while in a
> normal imperative language.

If I hit a call to map or to reduce, I've hit the bottom: map and reduce
are defined by Lisp and not by the programmer.

> And also I would like to point out that "hitting the language
> sooner", means to know every function in the standard Python library
> (which is by far uncomprehensible, its huge) and most of the times you
> also need the documentation. And if we go this path, when debugging we
> could use a smart IDE, which should show as tool-tip-text for function
> names their documentation, and in this way all we have to do to
> understand a particular function is just to point it out.

Although huge, at least the standard Python library is *bounded* (even if
only within a given release). The more I use it, the better I know it,
and there's probably an 80/20 rule in there somewhere (if I concentrate
on programs in my particular area(s) of interest, there may even be a
lurking 90/10 rule). Maybe I don't know the Standard Patterns well
enough (I still speak OO as a second language), but even when I see
something as apparently clear as add_listener(f), I have to wonder about
the specific semantics of your particular add_listener function.

Vsevolod

unread,
Apr 27, 2009, 10:40:50 PM4/27/09
to
On Apr 27, 8:18 pm, a...@pythoncraft.com (Aahz) wrote:
> That's because there's no response to make; the original post was a joke,
> and trying to have a serious discussion about it rarely excites people.

In every joke there's a grain of truth. And usenet is precisely for
that thing -- discussions. Even of stupid jokes. ;)

> If you want to talk about Python and problems you're running into, you
> should start a new thread.

I'm not at that level of proficiency with the language. I believe most
of my technical problems are connected with lack of knowledge or
experience, not the language's features. While conceptual problems
seem futile to discuss. There's another saying: "when in Rome do as
the Romans do"

Best regards,
Vsevolod

Vsevolod

unread,
Apr 27, 2009, 10:47:10 PM4/27/09
to
On Apr 27, 11:31 pm, David Bolen <db3l....@gmail.com> wrote:
> I'm curious - do you know what happens if threading is implemented as
> a native OS thread and it's stuck in an I/O operation that is blocked?
> How does the Lisp interpreter/runtime gain control again in order to
> execute the specified function? I guess on many POSIX-ish
> environments, internally generating a SIGALRM to interrupt a system
> operation might work, but it would likely have portability problems.

We're arguing to the old argument, who knows better, what the
programmer wants: language implementor or the programmer himself.
AFAIK, Python community is on former side, while Lisp one -- on the
later. As always, there's no right answer.

Best regards,
Vsevolod

It is loading more messages.
0 new messages