Skip to content
AgileLogics
5 min read

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.