3

In Python 3, reduce() has been moved to functools.reduce() and apparently it's better to use list comprehensions or plain loops for better readability.

I want to print the XOR'ed value of all elements in a list.

# My implementation with functools
from functools import reduce
print(reduce(lambda a, b: a^b, [1, 3, 2, 3, 4, 4, 5, 2, 1]))

And I have this:

# My implementation without functools
def XOR(x):
    ans = 0
    for i in x:
        ans = ans ^ i
    return ans

print(XOR([1, 3, 2, 3, 4, 4, 5, 2, 1]))

How can write a more functional version of this code without reduce()?

(Please provide references or code in Python 3, if any.)

Ajit Panigrahi
  • 752
  • 10
  • 27
  • 7
    Just stick with `reduce()`. This is an excellent case for it, **there is no reason not to use it**. – Martijn Pieters Jan 31 '18 at 18:14
  • 3
    Improvement: [`from operator import xor`](https://docs.python.org/3/library/operator.html#operator.xor), then use `reduce(xor, yourlist)`. – Martijn Pieters Jan 31 '18 at 18:14
  • 1
    @AjitZero built-in `xor` is most likely written in C, which involves less Python runtime. – Eli Korvigo Jan 31 '18 at 18:20
  • 3
    And, by the way, regarding "*reduce() has been moved to functools.reduce() and apparently it's better to use list comprehensions or plain loops for better readability*" – listcomps (and, more generally, generator expressions) can't replace `reduce` (the fold function), they target `map` and `filter`. `reduce` is a perfectly fine function, there is no reason for you to avoid it, if you don't adhere to Guido van Rossum's distaste for functional programming. – Eli Korvigo Jan 31 '18 at 18:25
  • Not doing any self-promotion, but [here is a purely functional reduce](https://stackoverflow.com/a/44071474/7051394), that uses only `lambda`s. Not that it's extremely useful... But I found it pretty relevant. – Right leg Jan 31 '18 at 18:25
  • @Rightleg I didn't mention performance considerations but there's lambda overhead to look out for (which I just learnt about). Thanks for the link! – Ajit Panigrahi Jan 31 '18 at 18:28
  • @AjitZero Oh. if you're looking for performance, just don't use this. If you're looking for readibility, too. Actually, don't use it :) – Right leg Jan 31 '18 at 18:30
  • @EliKorvigo I've been undecided on that since couldn't find an adequate replacement for `reduce()`; it seems like isn't such a big deal after all. Thanks! – Ajit Panigrahi Jan 31 '18 at 18:30
  • Why do you need a replacement for `reduce`? it still exists. – juanpa.arrivillaga Jan 31 '18 at 18:34
  • @juanpa.arrivillaga I was under the impression that it's no longer a recommended practice to use `reduce()`; Oh well. – Ajit Panigrahi Jan 31 '18 at 18:38

1 Answers1

10

Although Guido van Rossum didn't much care for reduce(), enough of the community did want it, which is why it was moved to functools and not removed outright. It is performant, and ideally suited to your use case. Just use it.

You can make your case faster and more readable by using operator.xor() to avoid the overhead of a new Python frame for a lambda:

from functools import reduce
from operator import xor

reduce(xor, [1, 3, 2, 3, 4, 4, 5, 2, 1])

Both xor() and reduce() are implemented in C. Calling back to the Python interpreter loop for the lambda is quite slow compared to calling another C function.

If you really must use a function, then use

def xor_reduce(values):
    result = 0
    for value in values:
        result ^= value
    return result

using in-place XOR, and better variable names.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • How do you feel about using `itertools.accumulate` in these situations? like `for x in accumulate([1, 3, 2, 3, 4, 4, 5, 2, 1], lambda a, b: a^b): pass` then `print(x)` – Chris_Rands Jan 31 '18 at 18:19
  • 2
    @Chris_Rands: if the goal is to have the intermediate values, sure. But that is a different use case; `functools.reduce()` is going to be faster if all you wanted was the *final result*. And I still would use `operator.xor()`. – Martijn Pieters Jan 31 '18 at 18:21