Path: csiph.com!usenet.pasdenom.info!news.redatomik.org!newsfeed.xs4all.nl!newsfeed8.news.xs4all.nl!post.news.xs4all.nl!not-for-mail Return-Path: X-Original-To: python-list@python.org Delivered-To: python-list@mail.python.org X-Spam-Status: OK 0.012 X-Spam-Evidence: '*H*': 0.98; '*S*': 0.00; '(b)': 0.07; 'correct.': 0.07; 'cc:addr:python-list': 0.09; 'todo:': 0.09; 'stack': 0.13; 'def': 0.13; '10:00': 0.16; 'call?': 0.16; 'cc:name:python': 0.16; 'chained': 0.16; 'cleaner': 0.16; 'from:addr:rosuav': 0.16; 'from:name:chris angelico': 0.16; 'iteratively': 0.16; 'parameters,': 0.16; 'programmers.': 0.16; 'result:': 0.16; 'traverse': 0.16; 'wrote:': 0.16; 'exists': 0.18; '>>>': 0.20; '2015': 0.20; 'cc:2**0': 0.20; 'cc:addr:python.org': 0.20; 'algorithm': 0.20; 'do.': 0.22; 'meant': 0.22; '(a)': 0.22; 'am,': 0.23; 'needed.': 0.23; 'implemented': 0.24; 'header:In-Reply- To:1': 0.24; 'mon,': 0.24; "i've": 0.25; "doesn't": 0.26; 'example': 0.26; 'sense': 0.26; 'chris': 0.26; 'message- id:@mail.gmail.com': 0.27; 'function': 0.28; 'looks': 0.29; '13,': 0.29; 'branches': 0.29; 'other,': 0.29; 'tail': 0.29; "i'm": 0.30; 'subject:/': 0.30; 'code': 0.30; 'another': 0.32; 'functional': 0.32; 'point': 0.33; 'problem': 0.33; 'call,': 0.33; 'received:google.com': 0.35; 'something': 0.35; 'asking': 0.35; "isn't": 0.35; 'but': 0.36; 'pm,': 0.36; 'subject:: ': 0.37; 'agree': 0.37; 'itself': 0.38; 'why': 0.39; 'does': 0.39; 'skip:e 20': 0.39; 'still': 0.40; 'your': 0.60; 'here.': 0.62; 'matter': 0.63; 'more': 0.63; 'strictly': 0.64; 'places': 0.64; 'worth': 0.67; 'eight': 0.72; 'jul': 0.72; 'away,': 0.84; 'chrisa': 0.84; 'iterative': 0.84; 'pardon': 0.84; 'positions:': 0.84; 'recursion,': 0.84; 'recursion;': 0.84; 'tree,': 0.84; 'to:none': 0.91 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:date:message-id:subject:from:cc :content-type; bh=2ePfKN2N/xL/Wmxs0Sz4E9oUgsOreIbNMkU5zdyVS2k=; b=Quej4oNtdp2+iCFEDn5S3Z+sb960GxUZBAkmBkqFJNXRb5pUUF2LaJD3/iZkbrqGzc +Jn8Z5388RWpNM6a07685nnTqgsZr1GlgTifUkMTqf9K1RrFVcD+Ze3PD330ZS+rJDnq /ZEgCu6cm1S7CjnSdKzaoP6wb8R2solqd0NZCwWflqzN2zh/S/PZ5hruyA5vQXhTgTQw fxbQRLk9+Qqu+L6l9XeAVhUo82zn6NcA3p5IjL7xxmdl05lRoY7pMO0+BuLIyWS51hjO Vjf9AovnODBNWX2C/AW5en4iNrdRThMfnOcurij4wstOexW8GHyhnUKvmTK6hdCFLY6W 9KZA== MIME-Version: 1.0 X-Received: by 10.107.9.142 with SMTP id 14mr21531096ioj.142.1436795811699; Mon, 13 Jul 2015 06:56:51 -0700 (PDT) In-Reply-To: References: <55A3A853.4040006@rece.vub.ac.be> Date: Mon, 13 Jul 2015 23:56:51 +1000 Subject: Re: Possibly Pythonic Tail Call Optimization (TCO/TRE) From: Chris Angelico Cc: Python Content-Type: text/plain; charset=UTF-8 X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.20+ Precedence: list List-Id: General discussion list for the Python programming language List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Newsgroups: comp.lang.python Message-ID: Lines: 51 NNTP-Posting-Host: 2001:888:2000:d::a6 X-Trace: 1436795813 news.xs4all.nl 2961 [2001:888:2000:d::a6]:33100 X-Complaints-To: abuse@xs4all.nl Xref: csiph.com comp.lang.python:93759 On Mon, Jul 13, 2015 at 11:46 PM, Ian Kelly wrote: > On Mon, Jul 13, 2015 at 6:34 AM, Chris Angelico wrote: >> On Mon, Jul 13, 2015 at 10:00 PM, Antoon Pardon >> wrote: >>> On 07/13/2015 01:28 PM, Chris Angelico wrote: >>>> Why is it worth writing your code recursively, only to have it be >>>> implemented iteratively? >>> >>> Because sometimes, it is easier to think about the problem recursively. >> >> Can you give me an example that (a) makes a lot more sense recursively >> than iteratively, and (b) involves just one tail call? > > Why does (b) matter? If the function has more than one tail call, it > doesn't matter which one you hit -- either way it's a tail call and > the stack frame is no longer needed. Oops. I meant "involves just one point of recursion". When you traverse a tree, only one can be a tail call, because after the other, you still have more processing to do. Most problems that I've found to work recursively and not iteratively have involved multiple branches - consider a simplistic solution to the Eight Queens problem that places a queen, then calls itself to place another queen: def eightqueens(positions=()): for i in range(8): if i not in positions: # TODO: check for the diagonals result = eightqueens(positions + (i,)) if result: return result return None While it can do the classic "call self, return result", it has to conditionally _not_ do that and keep on going. While this algorithm can be implemented iteratively, it's a reasonable show-case for recursion; but not for tail recursion, because one call to eightqueens() might result in more than one chained call, so I don't think there's any way for TCO to kick in here. What I'm asking for is an example of something that can have the tail calls optimized away, and yet still looks cleaner - preferably, *significantly* cleaner - in its recursive form than in its corresponding iterative form. Considering that an iterative function can maintain state that isn't in the parameters, I'm dubious that such a thing exists outside of the deranged minds of functional programmers. (Very much deranged. If you consider that a recursive factorial just uses addition and subtraction, while an iterative one can start with "for i in range(n):", you will agree that my terminology is strictly correct. So there.) ChrisA