Lambda question

Terry Reedy tjreedy at udel.edu
Mon Jun 6 12:52:31 EDT 2011


On 6/6/2011 9:42 AM, jyoung79 at 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





More information about the Python-list mailing list