On 6/6/2011 9:42 AM, jyoun...@kc.rr.com wrote:
f = lambda x, n, acc=[]: f(x[n:], n, acc+[(x[:n])]) if x else acc

Packing tail recursion into one line is bad for both understanding and
refactoring. Use better names and a docstring gives

def group(seq, n):
    'Yield from seq successive disjoint slices of length n plus the
remainder'
    for i in range(0,len(seq), n):
      yield seq[i:i+n]

[I added back the last 'n' that got deleted somehow]

Thank you all very much for this incredible help!  The original code
now makes sense, and I was thrilled to see better and more efficient
ways of doing this.  Thanks for taking the time to share your
thoughts as well as the excellent detail everyone shared…  I really
appreciate it!

You are welcome.

Let me add something not said much here about designing functions: start with both a clear and succinct definition *and* test cases. (I only started writing tests first a year ago or so.) Test cases help test the definition statement as well as the yet-to-be-written code. They also make re-factoring much safer. I think test cases should start with null inputs. For this function:

for inn,out in (
        (('',1), []), # no input, no output
        (('abc',0), []), # definition unclear, could be error
        (('abc',1), ['a','b','c']),
        (('abcd',2), ['ab','cd']),
        (('abcde',2), ['ab', 'cd', 'e']), # could change this
        ):
    assert list(group(*inn)) == out, (inn,out)

This fails with
ValueError: range() arg 3 must not be zero

I will let you think about and try out what the original code 'f=../ does with n=0. It is not good. A third problem with lambda expressions is no test for bad inputs. They were added to Python for situations where one needs a function as an argument and and the return expression is self-explanatory, clearly correct, and safe for any inputs it could get in the context it is passed into. For example, lambda x: 2*x.

This works:

def group(seq, n):
  'Yield from seq successive disjoint slices of length n & the remainder'
  if n<=0: raise ValueError('group size must be positive')
  for i in range(0,len(seq), n):
    yield seq[i:i+n]

for inn,out in (
        (('',1), []), # no input, no output
        #(('abc',0), ValueError), # group size positive
        (('abc',1), ['a','b','c']),
        (('abcd',2), ['ab','cd']),
        (('abcde',2), ['ab', 'cd', 'e']), # could change this
        ):
    assert list(group(*inn)) == out, (inn,out)

I have written a function test function that I will post or upload to PyPI sometime. It accepts i/o pairs with error 'outputs', like the one commented out above.

--
Terry Jan Reedy


--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to