[Edu-sig] Python for CS101

John Zelle john.zelle at wartburg.edu
Sat May 7 22:21:13 CEST 2005



Toby Donaldson wrote:
> On 5/6/05 8:47 PM, "John Zelle" <john.zelle at wartburg.edu> wrote:
> 

>>For the record, it's very easy in LISP to implement a Queue using a
>>cons-pair with car pointing to the front of a linked list and cdr
>>pointing to the back. Using such a structure, constant time enqueue and
>>dequeue is trivial without arrays or amortized analysis. LISP allows one
>>to easily build any linked structure you'd like. There is no restriction
>>to a linear list with only a head (car) pointer.
>>
>>Of course implementing something like a queue which has state
>>(side-effects) is not pure functional programming, but real LISP
>>programmers don't worry too much about that.
> 
> 
> Thanks for pointing this out; I did a bit of searching on the web and found
> an implementation of what you are referring to. I'd never seen this before.
> I guess I should know better than to doubt what's possible in Common LISP.
> :-) Here's the code:
> 
> (defun enqueue (obj queue)
>   "Enqueue an object. Return the queue."
>   (if (cdr queue)
>       (setf (cdr (cdr queue)) (list obj)
>             (cdr queue) (cdr (cdr queue)))
>     (setf (cdr queue) (list obj)
>           (car queue) (cdr queue)))
>   queue)
> 
> 
> (defun dequeue (queue)
>   "Dequeue an object. Return the object queued."
>   (when (cdr queue)
>     (prog1
>         (caar queue)
>       (if (eq (cdr queue)
>               (car queue))
>           (setf (car queue) nil
>                 (cdr queue) nil))
>       (setf (car queue) (cdr (car queue))))))
> 
> Again, I think it is still an example of a design flaw in LISP because it
> violates the precept "simple things should be done simply".
>  

This _is_ a simple implementation of an indefinitely long queue. A 
linked list with head and tail pointers is the classic way of solving 
this problem. You seem to have some bias against linked structures. They 
are not inherently more complicated than arrays, just different.

>
<snipped here>
> 
> One could argue that in, say, C, the simple way to implement a fixed-sized
> queue is as a circular array. If you believe that, then I would suggest that
> is an example of good design: the simple solution to a simple problem is
> also the most efficient.
> 
> 

Ah, the key here is that you are restricting yourself to a fixed size 
queue. Arguably a circular array is the best solution. However, it is 
not necessarily a trivial implementation. As anyone who has actually 
implemented this will tell you, one has to be very careful about how 
full and empty conditions are handled. It's not hard, but it is subtle. 
I've seen many flawed queue implementations.


> If one uses linked lists, then it is a flaw in Python. Personally, I have
> never needed a linked list in Python.
> 

I can't agree with this statement. There are many situations where a 
linked structure us the _right_ implementation of a particular 
abstraction. If you want an indefinitely large queue in Python with 
constant time operations, then a linked list is probably the best way to 
do it. Furthermore, it is very easy to implement this in Python. Using 
linked lists for this in no way illustrates any flaw in Python.

The reason you may not have seen linked lists in Python is that the 
built-in continguous implementation is so good. Still, there are cases 
where reference-based linked structures are a good thing: ordered 
structures with constant time insertion, graphs, trees, sparse matricies 
(perhaps), etc. Sometimes, the linked list is entirely hidden in the 
logic of the problem. I once did a backtracking solution to n queens in 
Python and discovered only after the fact that I had actually built a 
linked list.

> 
>>If your argument is that both kinds of structures should have exactly
>>the same interface, that adds to the beginner's confusiuon over
>>efficiency that you argued above. By only providing the operations that
>>are efficient for the particular structure, LISP helps the novice
>>programmer use the more appropriate structure for a given task.
> 
> 
> Interesting argument. Although in Common LISP the "nth" function retrieves
> list items in linear time, so inefficient list operations are provided.
> 

True enough. I was only saying that your arguments seemed to be 
inconsistent. Not claiming that Common Lisp had necessarily taken this 
approach.

> Do you think Python would be a better language for beginners if a fixed-size
> array data type was added to it that had a different interface than the
> current dynamic lists?
> 

No. The operations that are efficient on a fixed size array are the same 
operations that are efficient on Python lists. For example, your 
fixed-sized (circular) queue can easily be implemented using a Python 
list. I think we agree that the "right" thing in this case is that 
programmers should be aware of what the Python list does and, when 
necessary, be able to optimize their code to use efficient operations.

The important thing to keep in mind is that it is not a design flaw 
simply that a language makes it easy to write inefficient programs. This 
is ususally the sign of a higher-level language. It becomes easier to 
write an inefficient program simply because it is easier to write 
programs period. Again, the queue example shows this. It is absolutely 
trivial to implement using append and pop(0). If O(n) efficiency is good 
enough for the problem at hand, what's wrong with that?

--John


-- 
John M. Zelle, Ph.D.             Wartburg College
Professor of Computer Science    Waverly, IA
john.zelle at wartburg.edu          (319) 352-8360


More information about the Edu-sig mailing list