TL;DR: How can we “broadcast” a function over several nested Tuples such as `((1,2), 3) .+ ((1,2), 3)`

?

So, say I have several objects `x_i`

that are tuples of tuples and numbers (each of the tuples itself a tuple of tuples and numbers). For example `x_1 = ((1,2), 3)`

or `x_1 = (1, (2,3,(3,4)), (2,3))`

, etc… Now assume all `x_i`

have the same structure, just different numbers. How can we apply a function `f`

elementwise recursively, i.e. to arbitrary Tuple depth to a collection of `x_i`

?

Currently `((1,2), 3) .+ ((1,2), 3)`

fails because it does `+`

only at the first level, and tuples themselves cannot be summed. I know broadcast is what it is, and it’s not exactly this, so I’m looking for something different. I’d like it to yield `((1+1, 2+2), 3+3)`

etc. Is this possible? Can we make it work it with arbitrary tuple nesting?

Hopefully the question is clear, please ask otherwise.

If you’re doing math with tuples, consider using the `StaticArrays`

packages instead. It gives you immutable arrays with arithmetic and linear algebra defined on them.

That said:

```
_f(x, y) = x + y
_f(x::Tuple, y::Tuple) = _f.(x, y)
```

```
julia> t1 = ((1,2), (3, (5,6)), (7, 8), 9);
julia> _f(t1, t1)
((2, 4), (6, (10, 12)), (14, 16), 18)
```

For numbers and Tuples of numbers, the above will work. Use at your own risk!

3 Likes

Here’s a crazy hack:

```
julia> ⊕(x, y) = x .⊕ y
⊕(x::Number, y::Number) = x + y
⊕ (generic function with 2 methods)
julia> ((1, 2), 3) .⊕ ((4, 5), 6)
((5, 7), 9)
```

It may behave suprisingly with unmatched tuple lengths and will stack-overflow with non-number-containing things.

4 Likes

Thanks! Yeah, I was rather looking for a solution more like broadcast or map that does not require to touch the methods of the function f. But I guess these options are probably the simplest

If you only need to go down one level, you could just broadcast a broadcasted operator. Hopefully someday soon you’ll be able to do `(.+).(((1, 2), 3), ((4, 5), 6))`

or `broadcast(.+, ((1, 2), 3), ((4, 5), 6))`

, but until then you could do:

```
julia> broadcast((args...)->(+).(args...), ((1, 2), 3), ((4, 5), 6))
((5, 7), 9)
```

4 Likes

Very nice! I think I’ll mark @tomerarnon’s reply as solution, thanks again to both.

1 Like