Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #93763 > unrolled thread
| Started by | "Th. Baruchel" <baruchel@no.spam.gmx.dot.com> |
|---|---|
| First post | 2015-07-13 15:44 +0000 |
| Last post | 2015-07-17 10:06 +0200 |
| Articles | 19 on this page of 39 — 16 participants |
Back to article view | Back to comp.lang.python
A new module for performing tail-call elimination "Th. Baruchel" <baruchel@no.spam.gmx.dot.com> - 2015-07-13 15:44 +0000
Re: A new module for performing tail-call elimination Antoon Pardon <antoon.pardon@rece.vub.ac.be> - 2015-07-15 11:29 +0200
Re: A new module for performing tail-call elimination Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2015-07-16 18:07 +1000
Re: A new module for performing tail-call elimination Robin Becker <robin@reportlab.com> - 2015-07-16 10:13 +0100
Re: A new module for performing tail-call elimination Robin Becker <robin@reportlab.com> - 2015-07-16 10:28 +0100
Re: A new module for performing tail-call elimination Marko Rauhamaa <marko@pacujo.net> - 2015-07-16 12:56 +0300
Re: A new module for performing tail-call elimination Antoon Pardon <antoon.pardon@rece.vub.ac.be> - 2015-07-16 12:41 +0200
Re: A new module for performing tail-call elimination Steven D'Aprano <steve@pearwood.info> - 2015-07-17 04:58 +1000
Re: A new module for performing tail-call elimination Antoon Pardon <antoon.pardon@rece.vub.ac.be> - 2015-07-17 11:00 +0200
Re: A new module for performing tail-call elimination Chris Angelico <rosuav@gmail.com> - 2015-07-16 21:11 +1000
Re: A new module for performing tail-call elimination Jeremy Sanders <jeremy@jeremysanders.net> - 2015-07-16 13:29 +0200
Re: A new module for performing tail-call elimination Antoon Pardon <antoon.pardon@rece.vub.ac.be> - 2015-07-16 15:35 +0200
Re: A new module for performing tail-call elimination Chris Angelico <rosuav@gmail.com> - 2015-07-16 23:47 +1000
Re: A new module for performing tail-call elimination Paul Rubin <no.email@nospam.invalid> - 2015-07-17 20:06 -0700
Re: A new module for performing tail-call elimination Antoon Pardon <antoon.pardon@rece.vub.ac.be> - 2015-07-16 16:21 +0200
Re: A new module for performing tail-call elimination Chris Angelico <rosuav@gmail.com> - 2015-07-17 00:27 +1000
Re: A new module for performing tail-call elimination Antoon Pardon <antoon.pardon@rece.vub.ac.be> - 2015-07-16 17:14 +0200
Re: A new module for performing tail-call elimination Ian Kelly <ian.g.kelly@gmail.com> - 2015-07-16 10:17 -0600
Re: A new module for performing tail-call elimination Ethan Furman <ethan@stoneleaf.us> - 2015-07-16 10:54 -0700
Re: A new module for performing tail-call elimination Ethan Furman <ethan@stoneleaf.us> - 2015-07-16 11:02 -0700
Re: A new module for performing tail-call elimination Terry Reedy <tjreedy@udel.edu> - 2015-07-16 15:45 -0400
Re: A new module for performing tail-call elimination Ethan Furman <ethan@stoneleaf.us> - 2015-07-16 12:58 -0700
Re: A new module for performing tail-call elimination Robin Becker <robin@reportlab.com> - 2015-07-17 09:57 +0100
Re: A new module for performing tail-call elimination Alain Ketterlin <alain@universite-de-strasbourg.fr.invalid> - 2015-07-16 13:23 +0200
Re: A new module for performing tail-call elimination Terry Reedy <tjreedy@udel.edu> - 2015-07-15 17:19 -0400
Re: A new module for performing tail-call elimination Antoon Pardon <antoon.pardon@rece.vub.ac.be> - 2015-07-16 09:45 +0200
Re: A new module for performing tail-call elimination Terry Reedy <tjreedy@udel.edu> - 2015-07-16 15:34 -0400
Re: A new module for performing tail-call elimination Marko Rauhamaa <marko@pacujo.net> - 2015-07-16 22:45 +0300
Re: A new module for performing tail-call elimination Terry Reedy <tjreedy@udel.edu> - 2015-07-17 15:47 -0400
Re: A new module for performing tail-call elimination Marko Rauhamaa <marko@pacujo.net> - 2015-07-17 23:55 +0300
Re: A new module for performing tail-call elimination Terry Reedy <tjreedy@udel.edu> - 2015-07-17 20:40 -0400
Re: A new module for performing tail-call elimination Gregory Ewing <greg.ewing@canterbury.ac.nz> - 2015-07-19 10:39 +1200
Re: A new module for performing tail-call elimination Chris Angelico <rosuav@gmail.com> - 2015-07-18 10:47 +1000
Re: A new module for performing tail-call elimination Gregory Ewing <greg.ewing@canterbury.ac.nz> - 2015-07-19 10:39 +1200
Re: A new module for performing tail-call elimination Gregory Ewing <greg.ewing@canterbury.ac.nz> - 2015-07-19 10:39 +1200
Re: A new module for performing tail-call elimination Mark Lawrence <breamoreboy@yahoo.co.uk> - 2015-07-19 01:09 +0100
Re: A new module for performing tail-call elimination MRAB <python@mrabarnett.plus.com> - 2015-07-19 01:19 +0100
Re: A new module for performing tail-call elimination Marko Rauhamaa <marko@pacujo.net> - 2015-07-19 09:29 +0300
Re: A new module for performing tail-call elimination Antoon Pardon <antoon.pardon@rece.vub.ac.be> - 2015-07-17 10:06 +0200
Page 2 of 2 — ← Prev page 1 [2]
| From | Terry Reedy <tjreedy@udel.edu> |
|---|---|
| Date | 2015-07-16 15:45 -0400 |
| Message-ID | <mailman.607.1437075969.3674.python-list@python.org> |
| In reply to | #93914 |
On 7/16/2015 2:02 PM, Ethan Furman wrote:
> On 07/16/2015 06:35 AM, Antoon Pardon wrote:
>> Here is one way to do the odd, even example.
>>
>> def even(n):
>> return odd_even('even', n)
>>
>> def odd(n):
>> return odd_even('odd', n)
>>
>> def odd_even(fn, n):
>> while fn is not None:
>> if fn == 'even':
>> fn, n = (None, True) if n == 0 else ('odd', n - 1)
>> elif fn == 'odd':
>> fn, n = (None, False) if n == 0 else ('even', n - 1)
>> else:
>> raise ValueError("Unknown state: %s" % fn)
>> return n
>
> Wow -- definitely uglier and less easy to understand than the original
> (even if the original had had the error-checking).
What you call ugly is an example of the core of the proof that
alternation and indefinite while looping are enough for turing
completeness, and that no recursive syntax is needed. Another way to
put it is that while loops perform recursion in the operational sense,
as opposed to the syntactical sense.
--
Terry Jan Reedy
[toc] | [prev] | [next] | [standalone]
| From | Ethan Furman <ethan@stoneleaf.us> |
|---|---|
| Date | 2015-07-16 12:58 -0700 |
| Message-ID | <mailman.609.1437076730.3674.python-list@python.org> |
| In reply to | #93914 |
On 07/16/2015 12:45 PM, Terry Reedy wrote:
> On 7/16/2015 2:02 PM, Ethan Furman wrote:
>> On 07/16/2015 06:35 AM, Antoon Pardon wrote:
>
>>> Here is one way to do the odd, even example.
>>>
>>> def even(n):
>>> return odd_even('even', n)
>>>
>>> def odd(n):
>>> return odd_even('odd', n)
>>>
>>> def odd_even(fn, n):
>>> while fn is not None:
>>> if fn == 'even':
>>> fn, n = (None, True) if n == 0 else ('odd', n - 1)
>>> elif fn == 'odd':
>>> fn, n = (None, False) if n == 0 else ('even', n - 1)
>>> else:
>>> raise ValueError("Unknown state: %s" % fn)
>>> return n
>>
>> Wow -- definitely uglier and less easy to understand than the original
>> (even if the original had had the error-checking).
>
> What you call ugly is an example of the core of the proof that alternation and indefinite
> while looping are enough for turing completeness, and that no recursive syntax is needed.
> Another way to put it is that while loops perform recursion in the operational sense, as
> opposed to the syntactical sense.
And we add new constructs and new language features to make certain ways of thinking/programming easier -- async and await come to mind. ;) After all, we don't really even need while loops as we
could get by with if and goto.
--
~Ethan~
[toc] | [prev] | [next] | [standalone]
| From | Robin Becker <robin@reportlab.com> |
|---|---|
| Date | 2015-07-17 09:57 +0100 |
| Message-ID | <mailman.634.1437123479.3674.python-list@python.org> |
| In reply to | #93914 |
On 16/07/2015 17:17, Ian Kelly wrote: > On Thu, Jul 16, 2015 at 3:28 AM, Robin Becker <robin@reportlab.com> wrote: ............. >> >> .... >> I believe the classic answer is Ackermann's function >> >> http://demonstrations.wolfram.com/RecursionInTheAckermannFunction/ >> >> which is said to be not "primitive recursive" ie cannot be unwound into >> loops; not sure whether that implies it has to be recursively defined or can >> perhaps be broken down some other way. > that should have said "simple loops" > My recollection -- and it's been awhile since I've studied > computability theory so I may be distorting things here -- is that > primitive recursive functions can be computed using for loops, i.e. > loops where the number of iterations is bounded in advance, whereas > non-primitive recursive functions require while loops. > I'm guessing, but is the implication that for loops can be specified finitely in advance, but while loops need some processing in the calculation to determine termination? I'm an engineer :( -- Robin Becker
[toc] | [prev] | [next] | [standalone]
| From | Alain Ketterlin <alain@universite-de-strasbourg.fr.invalid> |
|---|---|
| Date | 2015-07-16 13:23 +0200 |
| Message-ID | <87egk8qrb0.fsf@universite-de-strasbourg.fr.invalid> |
| In reply to | #93857 |
Antoon Pardon <antoon.pardon@rece.vub.ac.be> writes: > On 07/13/2015 05:44 PM, Th. Baruchel wrote: >> Hi, after having spent much time thinking about tail-call elimination >> in Python (see for instance http://baruchel.github.io/blog/ ), I finally >> decided to write a module for that. You may find it at: >> >> https://github.com/baruchel/tco >> >> Tail-call elimination is done for tail-recursion as well as for >> continuation-passing style (cps) in a consistent syntax for both usages. >> >> Any tail-recursive function having the suitable format for being >> embeddable in the Y combinator can be used here with no change at all >> (with the main difference that tail-calls will be eliminated), but other >> continuations can also be added > > Can you explain how you would do mutual recursive functions? > > Suppose I start with the following: > > def even(n): > True if n == 0 else odd(n - 1) > > def odd(n): > False if n == 0 else even(n - 1) > > > How do I rewrite those with your module? I'm not sure the module mentioned above has provision for that, but in any case the Y combinator has a corresponding fixed-point combinator called Y*. There is quite a bit of theory behind this, anybody interested can google, e.g., "Y combinator mutual recursion", whose first result is: http://stackoverflow.com/questions/4899113/fixed-point-combinator-for-mutually-recursive-functions -- Alain.
[toc] | [prev] | [next] | [standalone]
| From | Terry Reedy <tjreedy@udel.edu> |
|---|---|
| Date | 2015-07-15 17:19 -0400 |
| Message-ID | <mailman.549.1436995214.3674.python-list@python.org> |
| In reply to | #93763 |
On 7/15/2015 5:29 AM, Antoon Pardon wrote:
> On 07/13/2015 05:44 PM, Th. Baruchel wrote:
>> Hi, after having spent much time thinking about tail-call elimination
>> in Python (see for instance http://baruchel.github.io/blog/ ), I finally
>> decided to write a module for that. You may find it at:
>>
>> https://github.com/baruchel/tco
>>
>> Tail-call elimination is done for tail-recursion as well as for
>> continuation-passing style (cps) in a consistent syntax for both usages.
>>
>> Any tail-recursive function having the suitable format for being
>> embeddable in the Y combinator can be used here with no change at all
>> (with the main difference that tail-calls will be eliminated), but other
>> continuations can also be added
>
> Can you explain how you would do mutual recursive functions?
> Suppose I start with the following:
>
> def even(n):
> True if n == 0 else odd(n - 1)
>
> def odd(n):
> False if n == 0 else even(n - 1)
>
> How do I rewrite those with your module?
I will not answer for Baruchel's tco module. However, combining the two
bodies and following the general rule of replacing tail recursive calls
with assignments inside a while loop gives us
def even(n):
return not odd(n)
def odd(n):
while True:
if not n:
return False
else:
n -= 1
if not n:
return True
else:
n -= 1
# which passes this test
print(even(0) is True, odd(0) is False,
even(1) is False, odd(1) is True,
even(2) is True, odd(2) is False,
even(5) is False, odd(5) is True,
even(6) is True, odd(6) is False)
I believe that this pattern should work with any set of mutually
recursive functions that always call each other in cyclic order. A more
elaborate version does not have this limitation.
--
Terry Jan Reedy
[toc] | [prev] | [next] | [standalone]
| From | Antoon Pardon <antoon.pardon@rece.vub.ac.be> |
|---|---|
| Date | 2015-07-16 09:45 +0200 |
| Message-ID | <mailman.572.1437032728.3674.python-list@python.org> |
| In reply to | #93763 |
On 07/15/2015 11:19 PM, Terry Reedy wrote: > On 7/15/2015 5:29 AM, Antoon Pardon wrote: >> >> Can you explain how you would do mutual recursive functions? >> Suppose I start with the following: >> >> def even(n): >> True if n == 0 else odd(n - 1) >> >> def odd(n): >> False if n == 0 else even(n - 1) >> >> How do I rewrite those with your module? > > I will not answer for Baruchel's tco module. However, combining the > two bodies and following the general rule of replacing tail recursive > calls with assignments inside a while loop gives us > > def even(n): > return not odd(n) > > def odd(n): > while True: > if not n: > return False > else: > n -= 1 > if not n: > return True > else: > n -= 1 > [ ... ] > > I believe that this pattern should work with any set of mutually > recursive functions that always call each other in cyclic order. A > more elaborate version does not have this limitation. > Nice of you to illustrate the warping involved. ;-) -- Antoon Pardon.
[toc] | [prev] | [next] | [standalone]
| From | Terry Reedy <tjreedy@udel.edu> |
|---|---|
| Date | 2015-07-16 15:34 -0400 |
| Message-ID | <mailman.606.1437075284.3674.python-list@python.org> |
| In reply to | #93763 |
On 7/16/2015 3:45 AM, Antoon Pardon wrote: > On 07/15/2015 11:19 PM, Terry Reedy wrote: >> On 7/15/2015 5:29 AM, Antoon Pardon wrote: >>> >>> Can you explain how you would do mutual recursive functions? >>> Suppose I start with the following: >>> >>> def even(n): >>> True if n == 0 else odd(n - 1) >>> >>> def odd(n): >>> False if n == 0 else even(n - 1) >>> >>> How do I rewrite those with your module? >> >> I will not answer for Baruchel's tco module. However, combining the >> two bodies and following the general rule of replacing tail recursive >> calls with assignments inside a while loop gives us >> >> def even(n): >> return not odd(n) >> >> def odd(n): >> while True: >> if not n: >> return False >> else: >> n -= 1 >> if not n: >> return True >> else: >> n -= 1 >> [ ... ] >> >> I believe that this pattern should work with any set of mutually >> recursive functions that always call each other in cyclic order. A >> more elaborate version does not have this limitation. >> > > Nice of you to illustrate the warping involved. ;-) Glad you appreciate it. To me, the warping is no more than, and perhaps less than, and certainly less obnoxious than, the warping required when using Baruchel's tco module. (Opinions can vary, of course.) The result will definitely run faster than with B's tco. -- Terry Jan Reedy
[toc] | [prev] | [next] | [standalone]
| From | Marko Rauhamaa <marko@pacujo.net> |
|---|---|
| Date | 2015-07-16 22:45 +0300 |
| Message-ID | <87lhefanui.fsf@elektro.pacujo.net> |
| In reply to | #93955 |
Nobody seemed to notice that I just posted a fairly typical tail call
function:
========================================================================
def setvalue(self, keyseq, value, offset=0):
try:
next = keyseq[offset]
except IndexError:
self.value = value
return
if next is Node.ANY:
raise KeyError()
try:
child = self.children[next]
except KeyError:
self.children[next] = child = Node()
child.setvalue(keyseq, value, offset + 1)
========================================================================
In the context, the tail call is at least as clear as the corresponding
while/for loop.
In the case of this function, tail call elimination is hardly needed
because the key sequence is probably not all that long (and if it were,
the accompanying lookup function would overflow the stack anyway). At
any rate, it demonstrates how the idiom has its place in Python.
Marko
[toc] | [prev] | [next] | [standalone]
| From | Terry Reedy <tjreedy@udel.edu> |
|---|---|
| Date | 2015-07-17 15:47 -0400 |
| Message-ID | <mailman.656.1437162474.3674.python-list@python.org> |
| In reply to | #93956 |
On 7/16/2015 3:45 PM, Marko Rauhamaa wrote:
>
> Nobody seemed to notice that I just posted a fairly typical tail call
> function:
Because non-recursive tail calls are completely normal. Example:
return len(self.children)
Even tail operations like
return a + b
are tail calls if rewritten as
return a.__add__(b)
(usually but not always equivalent) or rewritten as
return operator.add(a, b)
(always equivalent) or compiled by an implementation as an equivalent
internal function call.
It happens to be that CPython implements calls to symbol operator
functions by putting the bodies of such functions into a C switch inside
a while loop. It this compiles operator symbols into bytecodes that
cause a jump to the corresponding code.
--
Terry Jan Reedy
[toc] | [prev] | [next] | [standalone]
| From | Marko Rauhamaa <marko@pacujo.net> |
|---|---|
| Date | 2015-07-17 23:55 +0300 |
| Message-ID | <877fpya4hy.fsf@elektro.pacujo.net> |
| In reply to | #94027 |
Terry Reedy <tjreedy@udel.edu>: > On 7/16/2015 3:45 PM, Marko Rauhamaa wrote: >> Nobody seemed to notice that I just posted a fairly typical tail call >> function: > > Because non-recursive tail calls are completely normal. I don't know how recursion makes a difference but that one did happen to be recursive. It could easily have been replaced with a while loop but there were good aesthetic reasons to leave it recursive. Marko
[toc] | [prev] | [next] | [standalone]
| From | Terry Reedy <tjreedy@udel.edu> |
|---|---|
| Date | 2015-07-17 20:40 -0400 |
| Message-ID | <mailman.669.1437180088.3674.python-list@python.org> |
| In reply to | #94032 |
On 7/17/2015 4:55 PM, Marko Rauhamaa wrote:
> Terry Reedy <tjreedy@udel.edu>:
>
>> On 7/16/2015 3:45 PM, Marko Rauhamaa wrote:
>>> Nobody seemed to notice that I just posted a fairly typical tail call
>>> function:
>>
>> Because non-recursive tail calls are completely normal.
>
> I don't know how recursion makes a difference
There are two extremely important differences:
1. Direct recursion calls the function that contains the call. This is
what allows replacement of tail recursion with a loop within the same
function.
2. Almost all problems with stack overflow are due to recursion, whether
at the tail or within the body of a code block. Such problems are nearly
always with linear recursion (one recursive call per call until the base
case). Problems with branching recursion (multiple recursive calls per
call) are rare except for very deep trees and graphs.
> but that one did happen to be recursive.
Not directly, as needed for loop conversion.
class X: # omitted from original but needed below
def setvalue(self, keyseq, value, offset=0):
...
child.setvalue(keyseq, value, offset + 1)
child.setvalue is a new callable bound method object, call it 'bm', that
is different from the setvalue function. This tail call is not a tail
recursive call. The standard conversion of adding a while loop and
replacing the tail call with the assignment in the call, in this case
'offset = offset+1' will not work.
> It could easily have been replaced with a while loop
Given the equality stated below, then yes.
child.setvalue(*args) == bm(*args) calls (in 3.x)
bm.__func__(bm.__self__, *args)
If type(child) == type(self) == X, then bm.func == X.setvalue
and the indirect call is the same as
X.setvalue(child, keyseq, value, offset + 1)
making the bound method call indirectly recursive.
If we replace the bound method call in setvalue with the call to
setvalue itself, then we have a tail recursive call and can do the
standard replacement to get:
class X
def setvalue(self, keyseq, value, offset=0):
while True:
...
# child.setvalue(keyseq, value, offset + 1)
offset += 1
self = child
If children are not always instances of type(self), as when a tree has
separate Node and Leaf classes, then recursive calls to Node instances
must be separated from non-recursive Leaf calls before replacing the
recursive calls.
Having a good reason to rebind the first parameter within methods, as
above, is a good reason to not hide it (as some have proposed).
> but there were good aesthetic reasons to leave it recursive.
Once one learns that looping and recursive calls are two ways of doing
the same thing -- repeatedly executing a block of code with altered
inputs, then the aesthetic reasons are purely aesthetic.
I personally would probably initially write and test setvalue with
recursion. If I were releasing it for others to use in production code
(where aesthetics no longer break ties between equivalent correct
implementations), I would do the conversion to avoid possible stack
overflows. For cases like this, where only some parameters are rebound,
I might leave the original tail call in a comment as documentation, as I
did above.
--
Terry Jan Reedy
[toc] | [prev] | [next] | [standalone]
| From | Gregory Ewing <greg.ewing@canterbury.ac.nz> |
|---|---|
| Date | 2015-07-19 10:39 +1200 |
| Message-ID | <d102t8F8rq4U3@mid.individual.net> |
| In reply to | #94042 |
Terry Reedy wrote: > Problems with branching recursion (multiple recursive calls per > call) are rare except for very deep trees and graphs. And if your recursion is branching, tail calls won't help you, except in certain special cases (such as the quicksort example) where you can predict in advance which branches are safe to recurse down. -- Greg
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2015-07-18 10:47 +1000 |
| Message-ID | <mailman.670.1437180453.3674.python-list@python.org> |
| In reply to | #94032 |
On Sat, Jul 18, 2015 at 10:40 AM, Terry Reedy <tjreedy@udel.edu> wrote: > If children are not always instances of type(self), as when a tree has > separate Node and Leaf classes, then recursive calls to Node instances must > be separated from non-recursive Leaf calls before replacing the recursive > calls. More serious concern: If it's possible for one of the children to be an arbitrary subclass of that type, it could have overridden the setvalue method. To maintain the expectations, you absolutely have to do the full lookup and pass the message down the chain. ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Gregory Ewing <greg.ewing@canterbury.ac.nz> |
|---|---|
| Date | 2015-07-19 10:39 +1200 |
| Message-ID | <d102t5F8rq4U2@mid.individual.net> |
| In reply to | #94032 |
Marko Rauhamaa wrote: > I don't know how recursion makes a difference It makes a difference because people don't expect frames to disappear from tracebacks when they're not consciously doing recursion. -- Greg
[toc] | [prev] | [next] | [standalone]
| From | Gregory Ewing <greg.ewing@canterbury.ac.nz> |
|---|---|
| Date | 2015-07-19 10:39 +1200 |
| Message-ID | <d102t1F8rq4U1@mid.individual.net> |
| In reply to | #93956 |
Marko Rauhamaa wrote: > At any rate, it demonstrates how the idiom has its place in Python. Perhaps it does, but I think I'd still prefer it to be explicit. The call in Marko's example is not actually a tail call as written. To make it a tail call, a return would need to be added: > return child.setvalue(keyseq, value, offset + 1) To someone reading the code, it's not obvious why the return is there. It looks redundant, and is likely to get removed by someone who thinks it's a mistake. Using a dedicated keyword would make it clear that tail call behaviour is being relied upon, and avoid looking like a spurious return. -- Greg
[toc] | [prev] | [next] | [standalone]
| From | Mark Lawrence <breamoreboy@yahoo.co.uk> |
|---|---|
| Date | 2015-07-19 01:09 +0100 |
| Message-ID | <mailman.687.1437264610.3674.python-list@python.org> |
| In reply to | #94064 |
On 18/07/2015 23:39, Gregory Ewing wrote: > Marko Rauhamaa wrote: >> At any rate, it demonstrates how the idiom has its place in Python. > > Perhaps it does, but I think I'd still prefer it to be > explicit. > > The call in Marko's example is not actually a tail call > as written. To make it a tail call, a return would need > to be added: > > > return child.setvalue(keyseq, value, offset + 1) > > To someone reading the code, it's not obvious why the > return is there. It looks redundant, and is likely to > get removed by someone who thinks it's a mistake. A time to use perhaps the most abused part of any programming language, a comment? > > Using a dedicated keyword would make it clear that tail > call behaviour is being relied upon, and avoid looking > like a spurious return. > +1 -- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language. Mark Lawrence
[toc] | [prev] | [next] | [standalone]
| From | MRAB <python@mrabarnett.plus.com> |
|---|---|
| Date | 2015-07-19 01:19 +0100 |
| Message-ID | <mailman.689.1437265194.3674.python-list@python.org> |
| In reply to | #94064 |
On 2015-07-18 23:39, Gregory Ewing wrote:
> Marko Rauhamaa wrote:
>> At any rate, it demonstrates how the idiom has its place in Python.
>
> Perhaps it does, but I think I'd still prefer it to be
> explicit.
>
> The call in Marko's example is not actually a tail call
> as written. To make it a tail call, a return would need
> to be added:
>
> > return child.setvalue(keyseq, value, offset + 1)
>
> To someone reading the code, it's not obvious why the
> return is there. It looks redundant, and is likely to
> get removed by someone who thinks it's a mistake.
>
> Using a dedicated keyword would make it clear that tail
> call behaviour is being relied upon, and avoid looking
> like a spurious return.
>
Of the current reserved words, the choice is between 'continue' and
'pass'.
How clear is:
continue child.setvalue(keyseq, value, offset + 1)
?
I think the problem is that it reads like it's something to do with
co-routines.
[toc] | [prev] | [next] | [standalone]
| From | Marko Rauhamaa <marko@pacujo.net> |
|---|---|
| Date | 2015-07-19 09:29 +0300 |
| Message-ID | <87y4ic7j8m.fsf@elektro.pacujo.net> |
| In reply to | #94064 |
Gregory Ewing <greg.ewing@canterbury.ac.nz>: > Marko Rauhamaa wrote: >> At any rate, it demonstrates how the idiom has its place in Python. > > Perhaps it does, but I think I'd still prefer it to be explicit. Don't get me wrong. The method doesn't depend on tail call elimination. It couldn't because its sibling method has the same recursion depth without the possibility of tail call elimination. I was just showing an example of an iteration expressed idiomatically through a tail call. > The call in Marko's example is not actually a tail call as written. To > make it a tail call, a return would need to be added: > >> return child.setvalue(keyseq, value, offset + 1) Adding the return statement there would be bad because it would suggest to the maintainer of the code that the method has a meaningful return value, which it doesn't. > To someone reading the code, it's not obvious why the return is there. > It looks redundant, and is likely to get removed by someone who thinks > it's a mistake. Exactly. That's why I said also that it is unfortunate for Python to promise to return None implicitly. That stipulation of the language specification is against the de-facto semantics of procedures (your sentence above) and effectively prevents automatic tail call optimizations. > Using a dedicated keyword would make it clear that tail call behaviour > is being relied upon, and avoid looking like a spurious return. A dedicated keyword would indeed work as advertised. However, I'm not too keen on the idea. My position is: Python probably should have made tail call elimination a matter of course, but since it doesn't, oh well. Marko
[toc] | [prev] | [next] | [standalone]
| From | Antoon Pardon <antoon.pardon@rece.vub.ac.be> |
|---|---|
| Date | 2015-07-17 10:06 +0200 |
| Message-ID | <mailman.633.1437120370.3674.python-list@python.org> |
| In reply to | #93763 |
On 07/16/2015 09:34 PM, Terry Reedy wrote: > On 7/16/2015 3:45 AM, Antoon Pardon wrote: >> On 07/15/2015 11:19 PM, Terry Reedy wrote: >>> >>> I believe that this pattern should work with any set of mutually >>> recursive functions that always call each other in cyclic order. A >>> more elaborate version does not have this limitation. >>> >> >> Nice of you to illustrate the warping involved. ;-) > > Glad you appreciate it. To me, the warping is no more than, and > perhaps less than, and certainly less obnoxious than,the warping > required when using Baruchel's tco module. (Opinions can vary, > of course.) The result will definitely run faster than with B's tco. I don't care about the speed that much. Clarity of code is more important. And I agree how Baruchel's tco module needs to be used, doesn't seem very helpful in that respect. I wouldn't call it obnoxious, cause I can appreciate the mathematical elegance behind it, but my impression is that understanding what is going on, requires a back ground knowledge that is generally not expected. So using this would probably be good for job security but wouldn't be fair to my colleagues. -- Antoon Pardon
[toc] | [prev] | [standalone]
Page 2 of 2 — ← Prev page 1 [2]
Back to top | Article view | comp.lang.python
csiph-web