How do you have a multiple line statement in either a list comprehension or eval?
I was trying to turn this code:
def f(x, y, b=''):
for i in x:
if i in y:
y.remove(i)
i *= 2
b += i
return b
Into a lambda function like so:
j=lambda x,y:''.join(eval('y.remove(i);i*2')if i in y else i for i in x)
In both x
is a string such as 'onomatopoeia'
and y
is a list such as ['o','a','o']
.
But for some reason, it returns a syntax error. Can anyone explain this?
First, you probably shouldn't rewrite this with a lambda because of the side-effects in the loop. If you really want to anyway, don't use an eval.
I'd suggest:
j = lambda x, y: ''.join((y.remove(i) or 2 * i) if i in y else i for i in x)
Because the result of remove is None
the second argument to or
will be the result. This avoids the eval. But it's still worse then a for-loop.
As noted in comments on the original question, 2 * y.pop(y.index(i))
is more readable than the or
construct. You'll loop twice over y
, but performance doesn't seem the issue.
I would vastly prefer your function but this will do what you want.
from itertools import chain
j = lambda x, y: ''.join(filter(None,chain.from_iterable((i * 2,y.remove(i)) if i in y else i for i in x)))
print(j("'onomatopoeia'",['o','a','o']))
'oonoomaatopoeia'
If you want to write good functional expressions (using lambda
s, map
, reduce
, filter
and so on), you should avoid side effects.
I would strongly prefer your code as a function rather than a lambda with side effects.
This is a sideeffect-free implementation in one lambda expression:
>>> from functools import reduce
>>> (lambda x, y: reduce(lambda a, b: \
... (a[0]+2*b, a[1][:a[1].index(b)]+a[1][a[1].index(b)+1:]) if b in a[1] \
... else (a[0]+b, a[1]), x, ('',y))[0])('onomatopoeia', ['o','a','o'])
'oonoomaatopoeia'
I'm afraid it's not short nor beautiful nor easy to understand * as one would want it to be for a lambda. :/
(hopefully someone can suggest an improvement)
Just a counter-example to discourage the use of a lambda in this context.
IMHO the biggest problem with lambdas in python is that there is no where
syntax like in Standard ML to define variable aliases in the same expression. So, things get ugly pretty quick for anything non trivial.
If you are interested in understanding what it does, the idea is to use reduce to run an automata, where the result (at each step) is the "state" of the computation.
The initial "state" is ('', ['o','a','o'])
, and the reduce function will do the replacement as needed, starting from 'onomatopoeia'
.
This is the evolution of the "state":
( '', ['o','a','o'] ) 'o'
( 'oo', ['a','o'] ) 'n'
( 'oon', ['a','o'] ) 'o'
( 'oonoo', ['a'] ) 'm'
( 'oonoom', ['a'] ) 'a'
( 'oonoomaa', [] ) 't'
( 'oonoomaat', [] ) 'o'
( 'oonoomaato', [] ) 'p'
( 'oonoomaatop', [] ) 'o'
( 'oonoomaatopo', [] ) 'e'
( 'oonoomaatopoe', [] ) 'i'
( 'oonoomaatopoei', [] ) 'a'
( 'oonoomaatopoeia', [] )
and we take only first element of the last state.