'Boolean values list to integer in Python

I'm having trouble with this lambdas syntax. I'm trying to translate a list of booleans to an integer value, but I'm getting an error, I don't understand why.

Here is the code:

from functools import reduce

binary = [True, True, False, True]

L = list(range(len(binary)))      #Step 1
print(L)                          #[0, 1, 2, 3]
L = map(lambda a: 2**a, L)        #Step 2
print(L)                          #[1, 2, 4, 8]
L = zip(binary, L)                #Step 3
print(L)                          #[(True, 1),(True, 2),(False, 4),(True, 8)]
L = filter(lambda a, b: a, L)     #Step 4
print(L)                          #[(True, 1),(True, 2),(True, 8)] not sure
L = map(lambda a, b: b, L)        #Step 5
print(L)                          #?????
L = reduce(lambda a, b: a + b, L) #Final step 
print(L)                          #11

Output:

Traceback (most recent call last):
  File "C:/Users/axel_/PycharmProjects/Python_Subject_Exam/19_new_exam_binary_list_translation.py", line 27, in <module>
    L = reduce(lambda a, b: a + b, L)
TypeError: <lambda>() missing 1 required positional argument: 'b'
[0, 1, 2, 3]
<map object at 0x00000267FAFE5388>
<zip object at 0x00000267FAFE50C8>
<filter object at 0x00000267FAFE5248>
<map object at 0x00000267FAFE4EC8>

Process finished with exit code 1

Could anyone help me to debug?



Solution 1:[1]

I think this will solve your problem. I will write some comments in the code to help you understand:

from functools import reduce

binary = [True, True, False, True]
L = list(range(len(binary)))       #[0, 1, 2, 3]

L = map(lambda a: 2**a, L)         #[1, 2, 4, 8]

L = zip(binary, L)                 #[(True, 1), (True, 2), (False, 4), (True, 8)]

L = filter(lambda x: x[0], L)      #<--- an item from zip() is an ((unpacked)) tuple

L = map(lambda x: x[1], L)
L = reduce(lambda a, b: a + b, L)
print(L)                           #11

Solution 2:[2]

This is one of the challenges if you are new to using iterators.

The iterator is evaluated as each item is pulled off the iterator, this means that when you call reduce to combine the result all of the lambda expressions are evaluated at that time. In your example this is equivalent to the following expression:

L = list(range(len(binary)))
L = reduce(lambda a, b: a + b, 
    map(lambda a, b: b, 
        filter(lambda a, b: a, 
            zip(binary, 
                map(lambda a: 2**a, L)
            )
        )
    )
)
print(L)  

Confusing?

In your example the line L = filter(lambda a, b: a, L) #Step 4 has an error. A filter expression takes a callable expression that takes a single argument along with an iterable but because it is not evaluated to the end you receive a confusing error.

One approach (and a good one when debugging) is to wrap each step in a call to list to force the evaluation to occur on each line eg L = list(filter(lambda a, b: a, L)). This will make the location of the error more apparent.

Alternatively use a generator expression rather than filter/map eg:

# filter (return odd numbers between 1 and 10)
result_a = filter(lambda a: a % 2, range(1, 11))
result b = (a for a in range(1, 11) if a % 2)

# map (return powers of 2)
result_a = map(lambda a: 2**a, range(11))
result_b = (2**a for a in range(11))

All of the results are the same but generator expressions have another trick, you can use tuple unpacking allowing for:

result = (b for a, b in zip(binary, range(len(binary))) if a)

Further, Python has other builtins that can simplify the code even more.

Enumerate will return a counter plus each element from an iterable allowing the first 3 steps to be simplified to:

# 1 step 1, 2 and 3 can be simplified with enumerate
L = ((i**2, b) for i, b in enumerate(L))

Next sum can be used to replace reduce, sum adds together all numerical values in an iterable:

L = reduce(lambda a, b: a + b, L)
# is equivalent to
L = sum(L)

And putting it all together the entire sequence can be simplified to:

L = sum(2**i for i, b in enumerate(binary) if b)

Hope that helps.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2