Shorthand for [[1,2],[3,4]] .+ 1

question

#1

Is there a short alternative for [[1,2],[3,4]] .+ 1 now that +(::Vector,::Number) is deprecated:

julia> x = [[1,2], [3,4]]
2-element Array{Array{Int64,1},1}:
 [1, 2]
 [3, 4]

julia> x .+ 1
┌ Warning: `a::AbstractArray + b::Number` is deprecated, use `a .+ b` instead.
│   caller = _broadcast_getindex_evalf at broadcast.jl:579 [inlined]
└ @ Core ./broadcast.jl:579
┌ Warning: `a::AbstractArray + b::Number` is deprecated, use `a .+ b` instead.
│   caller = _broadcast_getindex_evalf at broadcast.jl:579 [inlined]
└ @ Core ./broadcast.jl:579
2-element Array{Array{Int64,1},1}:
 [2, 3]
 [4, 5]

julia> (.+).(x,1)
┌ Warning: .+ is no longer a function object, use `broadcast(+, ...)` instead.
│   caller = ip:0x0
└ @ Core :-1
2-element Array{Array{Int64,1},1}:
 [2, 3]
 [4, 5]

julia> x ..+ 1
ERROR: syntax: invalid operator "..+"

julia> x .(.+) 1
ERROR: syntax: space before "." not allowed in "x ."

Preferred type expression for a vector of vectors
Preferred type expression for a vector of vectors
#2

IMO we should really consider .. as recursive broadcasting. I don’t have a good answer to this other than that feature request.


#3

OTOH, .. is really useful as an operator, eg IntervalSets.jl.


#4

Name it whatever, but I find myself wanted recursive behavior in broadcast often so I’m surprised there’s not an easy way to do it.


#5

Shortest I can think of is

((x, y) -> x .+ y).([[1,2],[3,4]], 1)

#6

How about this: broadcast(::Function) is the broadcasted version of a function:

fd = broadcast(f)
fd(x,y) === f.(x,y)

Then we can do:

[[1,2],[3,4]] .broadcast(+) 1

We could even have .+ return broadcast(+) so that the following works:

[[1,2],[3,4]] .(.+) 1

#7
julia> +ᵥ(x,y) = x .+ y
+ᵥ (generic function with 1 method)

julia> [[1,2],[3,4]] .+ᵥ 1
2-element Array{Array{Int64,1},1}:
 [2, 3]
 [4, 5]

#8

I usually end up using one of the following approaches,

((x, y) -> x .+ y).(x, 1)
map(elem -> elem .+ 1, x)
[ elem .+ 1 for elem ∈ x ]

#9

If it wasn’t for infix, one could generalize this neatly:

julia> bc(f) = (args...) -> f.(args...)
bc (generic function with 1 method)

julia> (bc(+)).([[1,2],[3,4]], 1)
2-element Array{Array{Int64,1},1}:
 [2, 3]
 [4, 5]

And then bc(bc(f)), etc.


#10

That’s another example of Julia trying to be perfect. If a similar effort is being exerted to make it simple, Julia can place itself in the Top 5 list in just a few years. Before 0.7, this question wouldn’t have been asked. I mentioned this point in a previous long thread.

Past century languages allowed A + 1 by default without complaining. Fortran, Matlab, R, Numpy, APL, Perl, and others were created and used by genius mathematicians and no one, AFAIK, complained about adding a scalar to an array. My comment may seem negative but I love Julia and I try to be honest. These tiny perfections can accumulate and yield a perfect language that no one likes to use. The moral of the story? Simplicity, simplicity, simplicity, and putting the end-user in focus, always choosing the good approximation approach.


#11

It may surprise you (based on your apparently dim view of our decision making process), that the deprecation of A + 1 was not done just to annoy people and be pedantic, but with careful deliberation for mathematical reasons. Mathematicians tend to like + to be associative, which was no longer the case when A + 1 with Matlab-like behavior was allowed in combination with uniform scaling objects (I)—a uniquely Julian and very powerful feature. (It is left as an exercise for the reader to figure out how this causes associativity to fail or to find the issue where this decision was made.) In the showdown between A + 1 and A + I the former loses out because broadcasting behavior for scalars is not really mathematically sound: in generic code A + 1 should really behave the same as A + I instead of like A + ones(n,n) because I Is the multiplicative identity for arrays, not ones(n,n). The broadcasting behavior of + really only makes sense in the old Matlab (et al.) mindset where vectorization is necessary for performance, a view point that is neither mandatory nor even beneficial in Julia.


Julia 1.0 released
#12

I tend to agree with you that double dot might be too much syntax, and Julia needs to be careful not to introduce too much syntax. But dot broadcasting is a bad example to use.

My R using colleagues love Julias dot broadcasting. It’s so much clearer what is actually happening, and so much more powerful because the math is explicit and correct.


#13

If one needs to nest down many levels you can do

julia> function bc(f::Function, n::Int)
           if n > 1
               bc(f, n-1)
           elseif n == 1
               (args...) -> f.(args...)
           else
               throw("n must be a positive integer")
           end
       end
bc (generic function with 2 methods)

julia> bc(+, 2).([[1,2],[3,4]], 1)
2-element Array{Array{Int64,1},1}:
 [2, 3]
 [4, 5]

ie. the depth you need to broadcast is just specified by n. If the recursion is worrisome, perhaps using foldl is the way to go.


#14

How do you make it automatically go to the leaf?


#15

Is there a clear definition of “leaf” here?


#16

Broadcasting scalar type


#17

Are you asking how to automatically find the depth needed or are you asking how to work with an array of variable depth (ie. [1, [1,2]])?


#18

Automatically find the depth and broadcast at that depth.


#19

For this to work, we have to assume that f(x::Int) doesn’t also have a definition f(x::Vector). Otherwise, we wouldn’t know where to stop, right?

Otherwise we would have to pass a type signature or something. mean(x) could work on scalars or arrays.