Difficulties understanding reduce function in context

Screen Link:

https://app.dataquest.io/m/263/functional-programming/7/the-reduce-function

My Code:

lines = read('example_log.txt')
ip_addresses = list(map(lambda x: x.split()[0], lines))
filtered_ips = list(filter(lambda x: int(x.split('.')[0]) <= 20, ip_addresses))
count_all = reduce(lambda x, _: 2 if isinstance(x, str) else x + 1, lines)  

Can someone explain me why the lambda function of variable count_all should return 2 if isinstance(x, str) ?

3 Likes

This is explained in the given screen, from the sentence below, until the end of the learn section.

Another important aspect of reduce is that the lambda function does not necessarily need to return a value that is of the same type as the inputs

Let’s see a simpler example and explain what it is doing

>>> ip_head = [
... '200.155.108.44',
...  '36.139.255.202',
...  '50.112.115.219',
...  '204.132.56.4',
...  '233.154.7.24'
... ]

The goal is to count how many elements ip_head has, using reduce.

>>> reduce(lambda x, _: 2 if isinstance(x, str) else x + 1, ip_head)
5

Let’s see what this is doing. We’ll begin by analyzing the function lambda x,_: 2 if isinstance(x, str) else x + 1.

Let’s rewrite it as a regular function:

>>> def sum_if_not_string(x,y):
...     if isinstance(x, str):
...         return 2
...     else:
...         return x+1

This function returns 2 if the first argument is a string, otherwise it tries to add 1 to it.

>>> sum_if_not_string("Epstein didn't kill himself", 17)
2
>>> sum_if_not_string(20, 17)
21

Let’s see the behavior when we pass sum_if_not_string,ip_head to reduce.

>>> reduce(sum_if_not_string, ip_head)
5

It’s the same function, so we get the same behavior. Now that we’ve abstracted away the function, let’s focus on what it is doing with reduce.

First, it takes is as parameters the first two entries of ip_head. In other words, it’s calling sum_if_not_string('200.155.108.44', '36.139.255.202'). This returns 2 because the first argument is a string.

Next, because that’s how reduce works, the result we got above (2), and the third element of ip_head, are passed to sum_if_not_string. In other words, it calls sum_if_not_string(2, '50.112.115.219). This returns 3 because the first argument is not a string.

After this it calls 3 together with the fourth element of ip_head, returning 4. And to finalize, it runs sum_if_not_string(4, '233.154.7.24'), resulting in 5.

I hope this helps.

7 Likes

Great example, I got it thanks!

1 Like

This was a great breakdown!

1 Like

What if list has only one element?

You can just try it to get a sense of what happens. In any case, from the documentation:

Apply function of two arguments cumulatively to the items of iterable , from left to right, so as to reduce the iterable to a single value. For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates ((((1+2)+3)+4)+5) . The left argument, x , is the accumulated value and the right argument, y , is the update value from the iterable . If the optional initializer is present, it is placed before the items of the iterable in the calculation, and serves as a default when the iterable is empty. If initializer is not given and iterable contains only one item, the first item is returned.

2 Likes

Thank you so much !!

1 Like