alvas
alvas

Reputation: 122022

Lambda-ize functions that uses variables outside of the function

Given the function that takes x and manipulate x as such:

>>> x = [5,3,0,0]
>>> j = 1
>>> for i, xi in enumerate(x):
...     if xi == 0:
...             x[i] = 1.0/2**j
...             j+=1
... 
>>> x
[5, 3, 0.5, 0.25]

And in a function:

def f(x):
  j = 1
  for i, xi in enumerate(x):
    if xi == 0:
      x[i] = 1.0/2**j
      j+=1
  return x

I want to change it into a lambda function but how is that possible when it uses an extra variable that not in my loop?

Without the complication of j+=1 and considering j as a constant I could do this:

j = 1
f = lambda x: [1.0/2**j if xi == 0 else xi for i, xi in enumerate(x)]

But I need the j to change when it if statement is made. How can that be achieved in a lambda function?

Upvotes: 0

Views: 70

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1121584

You could make j an itertools.count() object; each time you call next() on it it'll yield the next value in the sequence:

from itertools import count

j = count(1)
f = lambda x: [1.0 / 2 ** next(j) if xi == 0 else xi for i, xi in enumerate(x)]

This works because you only ever ask for that next value when x == 0 is true.

However, you now need to reset j each time you want to use the lambda. You could incorporate it into your list comprehension as an extra one-element tuple to loop over:

f = lambda x: [1.0 / 2 ** next(j) if xi == 0 else xi
               for j in (count(1),)
               for i, xi in enumerate(x)]

All this is not all that readable. I'd stick with the def function object instead.

As a side note, you could use or to replace the .. if xi == 0 else xi expression; xi == 0 makes xi falsey:

f = lambda x: [xi or 1.0 / 2 ** next(j)
               for j in (count(1),)
               for i, xi in enumerate(x)]

Upvotes: 2

Related Questions