which one do you prefer? python with C# or java?
Terry Reedy
tjreedy at udel.edu
Fri Jun 15 14:45:02 EDT 2012
On 6/15/2012 1:03 PM, Tomasz Rola wrote:
> Last time I checked, Python didn't have linked lists - arrayed lists are
> nice, but their elements can't be automatically GC-ed (or, this requires
> very nontrivial GC algorithm), the easiest way I can think would be
> replacing them with None manually. I'm not sure if del is
> performance-nice.
Python does not come with a linked-list class, but one can easily use
tuples or lists with two items as linked-list cells. One can optionally
wrap such cell in a linked-list class. However, if array lists do the
job, which they usually do, using linked-lists will take more time and
space. The problem being discussed may be a case where they are useful
and make it easier to save space.
> Also, around the same time, Python couldn't do tail-call,
Nonsense. A tail call is a call temporally followed by a return. In
CPython bytecode, it would be a call followed by return. In Python code,
it is a call spatially preceded by 'return'. Any "return f(whatever)", a
common operation is a tail call.
I presume you actually mean that CPython does not automatically convert
tail calls into local assignments and a jump to reuse the existing
execution frame instead of a new one. True. A Python interpreter could
easily detect all tail calls at either compilation or execution, but
such conversions would erase call history, leaving gaps in exception
tracebacks and make debugging harder. Depending on your viewpoint, such
conversion might be considered a semantic change.
Selectively converting recursive tail calls has specific problems that
have been discussed on other threads, and it would *still* erase the
call history that one might need to debug. If you do branching
recursion, as with a tree, and there is an unexpected exception, you
most likely really do want to see the complete call path leading up to
the exception. In addition, it is a feature that non-terminating
recursions such as "def forever(): return forever()" get stopped.
In any case, a properly written linear tail-recursive function is,
usually, easily converted to an explicit while loop. So if you want
within-frame looping, write it explicitly. To illustrate one general
pattern:
def tail_rec(a, b=start): # use default arg to avoid nesting
if should_loop(a, b):
return tail_rec(A(a,b), B(a,b))
else:
return term(a, b)
def while_equiv(a, b=start):
while should_loop(a, b):
a, b = A(a,b), B(a,b)
else:
return term(a, b)
In practice, should_loop, A, and B will usually be in-line expressions
rather than calls. There may be additional statements after if, else,
and while headers. In while_equiv, move b=start into the body. Else is
typically omitted from the while version, but I prefer it in the
recursive version.
One downside of the space saving is that the history of a,b values is
invisible unless one add a debug print statement. Another is that the
forever function becomes
def forever():
while True: pass
and Python will never stop it without intervention.
> Even more cool, with lazy evaluation (like in Haskell) one can generate
> lists on a fly and process them like they were statically allocated.
Python iterators can do lazy evaluation. All the builtin classes come
with a corresponding iterator.
> Yes, you could also run it in a loop or simulate lazy-eval manually (with
> yield)
There is nothing simulated about yield. Python mostly does what you tell
it to do. You just have to learn how to tell it to do what you want.
--
Terry Jan Reedy
More information about the Python-list
mailing list