4

I have an iterable delta that generates tuple of two numbers (dx, dy), and I want to compute the sum of each. The following doesn't work since delta is disposed after the first iteration.

x = sum(dx for dx, dy in delta)
y = sum(dy for dx, dy in delta)

Any idea? I'm thinking in the direction of somehow turning delta into two iterables of dx and dy, but have reached nothing so far.

Mazdak
  • 105,000
  • 18
  • 159
  • 188
Lingxi
  • 14,579
  • 2
  • 37
  • 93
  • [This](https://stackoverflow.com/questions/11280536/how-can-i-add-the-corresponding-elements-of-several-lists-of-numbers) question is related to yours, have a look at this one too. – Sohaib Farooqi Feb 03 '18 at 14:44

2 Answers2

6

Use zip() and map() functions to apply the sum() on each column:

x, y = map(sum, zip(*delta))
Mazdak
  • 105,000
  • 18
  • 159
  • 188
  • 1
    The solution is very nice. But a quick question: will `zip(*delta)` fully consume `delta` and load the whole content into memory at once? – Lingxi Feb 03 '18 at 14:21
  • @Lingxi Not in Python-3 but in Python-2 `zip` returns a list. In Python-2 you should use `itertools.izip()`. – Mazdak Feb 03 '18 at 14:22
  • @Kasramvd Very good solution. @Lingx You could also create n independent iterators from a single iterable using `itertools.tee` – Abdul Niyas P M Feb 03 '18 at 14:29
  • 1
    I think `zip()` doesn't consume it at once, but `*delta` does. No? – Lingxi Feb 03 '18 at 14:31
  • @Lingxi No I don't believe if it does. But still the way that python deals with your iterable depends on the type the functions you're using. In this case since built-in functions are embeded in C a built-in loop will consume your items one by one it's not like it loops multiple times on your iterable. – Mazdak Feb 03 '18 at 14:35
  • @Lingxi Which version of python you're using? – Mazdak Feb 03 '18 at 14:35
  • @Kasramvd I'm using CPython 3.6.4 – Lingxi Feb 03 '18 at 14:38
  • @vaultah `print()` is not a reliable example because it's not supposed to loop over the arguments and its nature is completely different than a function like `zip()`. – Mazdak Feb 03 '18 at 14:41
  • @vaultah This doesn't proof that `it` is consumed by the `*` or by the `zip`. Nevertheless, I think the best way is to take a look at the source code. – Mazdak Feb 03 '18 at 14:49
  • @vaultah Also I should note that `*` will definitely consume the iterator at python level. The thing that I'm not sure about is the way it behaves with built-in functions. – Mazdak Feb 03 '18 at 14:54
  • 1
    Just throwing it out there (as a rather bizarre option.... but appears to work): `sum((Counter({'a': a, 'b': b}) for a, b in d), Counter())` – Jon Clements Feb 03 '18 at 14:58
  • @JonClements What is `d` supposed to be? Isn't this a "Shlemiel The Painter"-based algorithm? – Mazdak Feb 03 '18 at 15:04
  • @Kasramvd in context I think it's obvious what it is... (plus I'm too lazy to type out delta on mobile) – Jon Clements Feb 03 '18 at 15:05
  • @vaultah I really hope that it's not so, but It's more likely that it is. I couldn't find anything helpful (hopeful) in source. Neither in built-in source nor in parser. – Mazdak Feb 03 '18 at 15:06
  • @JonClements No problem I got it. Although it's terribly inefficient but can be a good example to teach the summability of `Couner` objects lol. – Mazdak Feb 03 '18 at 15:08
  • 1
    Of course `a, b = reduce(lambda a, b: (a[0] + b[0], a[1] + b[1]), d)` works with directly iterating and storing rather than relying on unpacking, but hardly the greatest readability... – Jon Clements Feb 03 '18 at 15:15
  • For the technical details of how `*` unpacking is built into the function call syntax directly, there is a good answer from user2357112 [here](https://stackoverflow.com/a/22365935/674039). However, I'm not sure exactly how much of that is CPython implementation detail, and how much freedom other implementations might be given to do things differently. – wim Feb 03 '18 at 18:39
  • @JonClements I actually quite like your `reduce()` solution. – Lingxi Feb 04 '18 at 01:12
3

This ought to do it!

ysum = 0
xsum = 0

for dx, dy in delta:
    xsum += dx
    ysum += dy

The idea with a generator is that you can exhaust it once, so why not just do all of your math in one go?

blacksite
  • 12,086
  • 10
  • 64
  • 109