Forgive me for any problems in this e-mail as I'm new to this mailing list.<div><br></div><div>I thought it might be nice to be able to somehow save a state in list/generator comprehensions,</div><div>a side effect of this (although not the intended goal) is it would make reduce feasible in a clean manner as the final result would just be the state.</div>
<div><br></div><div>One mechanism I can think of is to overload the with/as keyword for use inside of list/generator comprehensions, and using the previous result as state</div><div>I believe the change to the grammar in python3k would be</div>
<div><br></div><div>comp_iter : comp_for | comp_if | comp_with</div><div>comp_with: 'with' testlist 'as' testlist<br><br>So something in the form of</div><div>  [expr for i in iterable with initializer as accumulator]</div>
<div>would resolve to something like</div><div>  result = []</div><div>  accumulator = initializer</div><div>  for i in iterable:</div><div>    accumulator = expr</div><div>    result.append(accumulator)</div><div>  return result</div>
<div><br>For instance reduce could be defined as (assuming all 3 arguments are required)</div><div>  reduce = lambda function, iterable, initializer : ([initializer] + [function(accumulator, i) for i in iterable with <span style="background-color:transparent">initializer as </span><span style="background-color:transparent">accumulator])[-1]</span></div>
<div>Breaking this down, the "with initializer as accumulator" statement means that when the list comprehension begins accumulator=initializer,</div><div>then after each iteration, accumulator = function(accumulator, i), so with the function f, list [i1,i2,i3,...], and initial value i0, the resulting list of</div>
<div><span style="background-color:transparent">"[function(accumulator, i) for i in iterable with initializer as accumulator]" </span>would be [f(i0,i1), f(f(i0,i1),i2), f(f(f(i0,i1),i2),i3),...], or in left associative infix form with</div>
<div>the f = "+" operator, [i0+i1,i0+i1+i2,i0+i1+i2+i3,...].<br>Consing (effectively) initializer to the beginning of the list ensures clean behavior for empty lists, and indexing [-1] gets the last element which is really the only</div>
<div>element that matters.</div><div><br></div><div>Consider a slightly more complex example of a Fibonacci generator, one might define it as follows,</div><div><div>  def fibs():</div><div>    a, b = 1, 0</div><div>    while True:</div>
<div>      a, b = b, a + b</div><div>      yield b</div></div><div><br></div><div>Using the with statement, it would require two generator comprehensions<br>  fibs = lambda : (b for a,b in (b, a+b for i in itertools.cycle((None,)) with a,b = 0,1))<br>
The inner generator comprehension</div><div>  <span style="background-color:transparent">(b, a+b for i in itertools.repeat(None) with a,b = 0,1)<br>creates an infinite generator of tuples which are consecutive Fibonacci numbers, the outer list comprehension strips off the unneeded "state".<br>
<br>Some of the pros of doing it this way is that because with/as are already keywords in python backwards compatibility shouldn't be an issue,<br>but if one is just mapping with state then an extra list/generator comprehension block is needed to strip the state from the intermediate list.<br>
<br>I apologize if similar ideas have already been discussed.</span></div><div>-Andrew Carter</div><div><br>p.s. Is there a built-in way to get the last element from a generator (perhaps even with a default) a quick google search did not reveal one?</div>
<div><span style="background-color:transparent"><br><br></span></div><div><br></div>