Explanation of the @. syntax

I came across this piece of syntax that was not immediately obvious to me.


sum(@. (data > 0.5) & (data < 0.75)) / length(samples)

I worked out that this is “mapping” the dot operator over all the other operators. I.e. the same result is obtained by:


sum( (data .> 0.5) .& (data .< 0.75)) / length(samples)

I know this is related to meta programming, which I am not good at. Could someone explain the mechanism of this?

Thx

Basically @. goes through the expression and inserts dots on all the functions in the expression.

1 Like

It’s not really metaprogramming, other than that the person who implemented @. was metaprogramming. Crash course:

A macro (@something) takes an expression as input, returns another expression and evaluates it.

So when you write @. x^2 - 2 * y + f(x), you give @. the argument :(x^2 - 2 * y + f(x)). The internal logic of @. looks at each operator and dots it, and thus returns :(x.^2 .- 2 .* y .+ f.(x)), which is now evaluated, so @. x^2 - 2 * y + f(x) is equivalent to x.^2 .- 2 .* y .+ f.(x).

10 Likes

You can use @macroexpand to see the code generated by a macro.

julia> data = rand(4);

julia> @macroexpand sum(@. (data > 0.5) & (data < 0.75)) / length(data)
:(sum((&).((>).(data, 0.5), (<).(data, 0.75))) / length(data))
10 Likes

Thank you! Oscar for confirming my interpretation, John for the useful advice re: @macroexpand. Your replies were very helpful. I will mark Gustaphe’s reply as solution - I hadn’t realized! Appreciate it.

And just in case you (or anyone else) hasn’t seen it, this blog post gives a really excellent explanation of why you’d want to dot every operator in an expression.

1 Like