For example, here:
ys = lift(frequency, phase) do fr, ph
@. 0.3 * sin(fr * xs - ph)
end
from here.
I am failing to interpret it as a macro definition or call.
This is the essence of a symbol: a symbol is used to represent a variable in metaprogramming. Once you have symbols as a data type, of course, it becomes tempting to use them for other things, like as hash keys. But that's an incidental, opportunistic usage of a data type that has another primary purpose.
Short-Circuit Evaluation The && and || operators in Julia correspond to logical “and” and “or” operations, respectively, and are typically used for this purpose.
There are two different ways to use the double colon “::” in Julia. First, it can be used to check to make sure a variable is of the correct type. Secondly, it can be used to force a local variable to always (locally) be of a certain data type. The syntax for making sure the variable is the correct type is “x::type”.
Metaprogramming may be defined as the programming in which we write Julia code to process and modify Julia code. With the help of Julia metaprogramming tools, one can write Julia programming code that modifies other parts of the source code file. These tools can even control when the modified code runs.
TLDR: @
invokes a macro. https://docs.julialang.org/en/v1/manual/metaprogramming/
One of julia's best features in my opinion are it's macros. They allow you to easily write functions that manipulate source code. @.
for example turns 0.3 * sin(fr * xs - ph)
into 0.3 .* sin(fr .* xs - ph)
. Another common example is @time
which roughly would translate the same expression to
t1=time()
0.3 * sin(fr * xs - ph)
println(time()-t1)
Note that neither of these are achievable by a function, since functions have their inputs evaluated before they run, while macros instead operate on the code itself.
I think it is worth to add that it is easy to learn what @.
does by invoking help. Press ?
then write @.
and hit enter to get:
help?> @.
@. expr
Convert every function call or operator in expr into a "dot call" (e.g.
convert f(x) to f.(x)), and convert every assignment in expr to a "dot
assignment" (e.g. convert += to .+=).
If you want to avoid adding dots for selected function calls in expr, splice
those function calls in with $. For example, @. sqrt(abs($sort(x))) is
equivalent to sqrt.(abs.(sort(x))) (no dot for sort).
(@. is equivalent to a call to @__dot__.)
Examples
≡≡≡≡≡≡≡≡≡≡
julia> x = 1.0:3.0; y = similar(x);
julia> @. y = x + 3 * sin(x)
3-element Array{Float64,1}:
3.5244129544236893
4.727892280477045
3.4233600241796016
where you also learn that @.
is just a shorthand for @__dot__
.
And if you want to find its definition write e.g.:
julia> @which @. 1
@__dot__(__source__::LineNumberNode, __module__::Module, x) in Base.Broadcast at broadcast.jl:1241
to get the exact information on location of its implementation or write @edit @. 1
and it will get opened-up in your editor.
Above I have commented on how to learn what @.
does and how it is implemented. If you want to learn what is the effect of @.
(or any macro in general) you can use the @macroexpand
macro. Therefore if you write e.g.:
julia> @macroexpand @. coalesce(sin(@view x[:, 1]), 0.0)
:(coalesce.(sin.(true && (view)(x, :, 1)), 0.0))
you can see how @.
macro rewrites your original expression coalesce(sin(x), 0.0)
. Note that in this case the expressions are not evaluated - you only get an equivalent expression with all macros removed.
If you wanted to see only @.
macro expanded (assuming - as in an example above it is the outermost macro) then use:
julia> @macroexpand1 @. coalesce(sin(@view x[:, 1]), 0.0)
:(coalesce.(sin.(#= REPL[11]:1 =# @view(x[:, 1])), 0.0))
As you can see @macroexpand1
is not recursive and only expanded the outermost macro and left @view
macro untouched.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With