Broadcasting in Depth
Prerequisites: Matrices and multidimensional arrays
Broadcasting (the dot syntax from Level 1) also stretches arrays of different shapes to match. A dimension of length 1 is repeated to fit:
col = [1, 2, 3] # 3-element column
row = [10 20] # 1×2 row
display(col .+ row)
display(col .* row)
M = [1 2; 3 4]
display(M .- [1, 3]) # subtract a different value from each row
3×2 Matrix{Int64}:
11 21
12 22
13 23
3×2 Matrix{Int64}:
10 20
20 40
30 60
2×2 Matrix{Int64}:
0 1
0 1
A column (3×1) plus a row (1×2) gives a 3×2 matrix — a quick way to build tables like a multiplication table.
Normalizing columns
using Statistics
data = [1.0 10.0; 2.0 20.0; 3.0 30.0]
mu = mean(data, dims = 1)
sigma = std(data, dims = 1)
z = (data .- mu) ./ sigma
display(z)
3×2 Matrix{Float64}:
-1.0 -1.0
0.0 0.0
1.0 1.0
Each column is standardized to mean 0 and standard deviation 1 in one
line — no loops, no manual repetition of mu.
Fusion and in-place updates
Julia fuses a whole dotted expression into one loop. With .=, the
result is written into an existing array, so nothing new is allocated:
x = collect(1.0:5.0)
y = zeros(5)
y .= 2 .* x .+ 1
println(y)
@. y = sqrt(y) + x^2
println(round.(y, digits = 3))
[3.0, 5.0, 7.0, 9.0, 11.0]
[2.732, 6.236, 11.646, 19.0, 28.317]
@. y = ... also turns the = into .=. This matters for performance
in loops — Level 3 measures it.
Protecting arguments from broadcasting
Sometimes an argument should be treated as a single value, not iterated.
Wrap it in Ref (or a one-element tuple):
words = ["apple", "kiwi", "plum"]
allowed = ["kiwi", "plum", "fig"]
println(in.(words, Ref(allowed)))
println(occursin.("i", words))
Bool[0, 1, 1]
Bool[0, 1, 0]
Without Ref, Julia would try to pair words[1] with allowed[1] and
so on.