First of all to install julia from homebrew:
$ brew cask install julia
Another way is using a tap from a guy in the JuliaLang community of github:
$ brew tap staticfloat/julia
$ brew install julia
To get Julia run with iPython/jupyter notebooks, simply open the julia prompt by
$ julia
Then use julia's package manager to get IJulia
package.
$ julia > Pkg.add("IJulia")
To install Julia packages run the Pkg.add()
function!
For example
$ Pkg.add("PyPlot") # to get the package to make plots as Matplotlib
This will download all the dependencies needed and include the PyPlot
package in the current Julia directory.
In [1]:
2+3
Out[1]:
In [2]:
4./2.
Out[2]:
In [3]:
4/2
Out[3]:
help?> fft
search: fft fft! FFTW fftshift rfft ifft bfft ifft! bfft! ifftshift irfft brfft plan_fft plan_fft!
fft(A [, dims])
Performs a multidimensional FFT of the array A. The optional dims argument specifies an iterable
subset of dimensions (e.g. an integer, range, tuple, or array) to transform along. Most
efficient if the size of A along the transformed dimensions is a product of small primes; see
nextprod(). See also plan_fft() for even greater efficiency.
A one-dimensional FFT computes the one-dimensional discrete Fourier transform (DFT) as defined
by
\operatorname{DFT}(A)[k] =
\sum_{n=1}^{\operatorname{length}(A)}
\exp\left(-i\frac{2\pi
(n-1)(k-1)}{\operatorname{length}(A)} \right) A[n].
A multidimensional FFT simply performs this operation along each transformed dimension of A.
Higher performance is usually possible with multi-threading. Use FFTW.set_num_threads(np) to use
np threads, if you have np processors.
Numeric values are similar to Python
Variables are similar to Python, but any unicode string can be used as variable name! Even latex notation!
In [4]:
x=3
Out[4]:
In [5]:
α = 4
Out[5]:
In [231]:
ℵ = 5
Out[231]:
In [7]:
println("x = ",x)
In [8]:
f(x) = 2x^2 + 3x +1
Out[8]:
In [9]:
g(x) = f(x) - 2f(x-2)
Out[9]:
In [10]:
g(2)
Out[10]:
In [11]:
f(2)
Out[11]:
The value of variables may be substituted inside strings with the $
operator.
In [12]:
name = "Nikos"
Out[12]:
In [13]:
my_greeting = "Hello $name"
Out[13]:
In [14]:
println(my_greeting)
more complicated stuff can be used with wrapped parenthesis:
In [15]:
a = 3
Out[15]:
In [16]:
println("The sine of $a is $(sin(a))")
In [17]:
a = Int8(1)
Out[17]:
In [18]:
b = Int16(2)
Out[18]:
In [19]:
a+b
Out[19]:
Arbitrary-precision integers and floiting points are available through the types BigInt()
and BigFloat()
. The function big()
converts a number into the corresponding Big type.
In [20]:
big(10)
Out[20]:
In [21]:
typeof(ans)
Out[21]:
Unlike Python, integers are NOT automatically promoted to arbitrary-precision integers.
In [22]:
a=7
c = (1+3im)*a
Out[22]:
To get the real and imaginary parts:
In [23]:
c.re
Out[23]:
In [24]:
c.im
Out[24]:
In [25]:
c.re, c.im ## this is a tuple, just as Python
Out[25]:
The conjugate of a complex is given by the conj()
function:
In [26]:
conj(a)
Out[26]:
In [27]:
conj(c)
Out[27]:
You can use rational numbers (fractions) with the built in \\
operator:
In [28]:
3//4
Out[28]:
In [29]:
3//4 + 5//6
Out[29]:
In [30]:
typeof(ans)
Out[30]:
Operators are convenienet way of writing functions
In [31]:
//(3,4) ## you could write +(3,4) -> ans = 7
Out[31]:
In [32]:
//
Out[32]:
In [33]:
methods(//) # it will list all the things you can done with this
# function or operator
Out[33]:
The expression n::Integer
is a type annotation that specifies that the method applies when its first argument is of type Integer
In [34]:
2.4//1.4 ## cannot do this with floats
In [35]:
(3//4)^20
Out[35]:
In [36]:
(3//4)^50 # when numbers getting big it will overflow
but using bigInts...
In [37]:
(big(3)//4)^50
Out[37]:
In [38]:
typeof(ans)
Out[38]:
To store several variables in one, use a "list" as in Python:
In [39]:
l = [3,4,5]
Out[39]:
In [40]:
typeof(l)
Out[40]:
In Julia, these are called Arrays.
The string Array{Int64,1}
gives you the type of the array and the dimension of it.
All elements of the array, it must be of the same type!
In [41]:
k = [1,3, 9.2] # converts them all to float to be of the same type
Out[41]:
In [42]:
m = [1, 4, "w00t"] # that is of `Any` type
Out[42]:
In [43]:
n = [1.4, 'a']
Out[43]:
In [44]:
o = {3., 4, "hi", [2, 1]}
Out[44]:
So you can make lists of mixed types, but if used for numerical calculations you lose the efficiency of "numpy".
In [45]:
k[1]
Out[45]:
In [46]:
k[0]
For range:
In [47]:
k[1:3] # this will give you k[1], k[2] AND k[3]
Out[47]:
Unlike Python the limits (start, end) must be mentioned explicitly:
In [48]:
k[1:end]
Out[48]:
In [49]:
k[1:end-1]
Out[49]:
In [50]:
k[-1] # does not work
In Julia Arrays
, like Python lists, are dynamic, which is not the case for numpy arrays.
But the syntax is a bit different.
To add an element at the end of the list:
In [51]:
k
Out[51]:
In [52]:
k+k
Out[52]:
In [53]:
push!(k,7) # the ! is a convention of Julia, which means that it will modify the argument
Out[53]:
In [54]:
methods(push!)
Out[54]:
In [55]:
methodswith(Array) #all the things I can do with the type Array
Out[55]:
In [56]:
append!(k,[2.,3.,4.]) # similar to push! but for multiple variables at once
Out[56]:
Arrays work like mathematical vectors .
In [57]:
a = [1.1, 2.2, 3.3]
Out[57]:
In [58]:
b = [4.4, 5.5, 6.6]
Out[58]:
In [59]:
a+b
Out[59]:
In [60]:
b-a
Out[60]:
In [61]:
(2.5 * a )
Out[61]:
However, operations between arrays are not permitted because of their loose definition. (What does it mean to you a*b
? Inner vector? Product of each element?)
In [62]:
a*b
HOWEVER for element-wise operations you can use the Matlab-syntax .
In [63]:
a.*b
Out[63]:
But there are also so many usefull built-in functions:
In [64]:
dot(a,b) # dot product
Out[64]:
In [65]:
a ⋅ b # using \cdot<TAB>
Out[65]:
In [66]:
cross(a,b) # cross product of vectors
Out[66]:
In [67]:
a×b # \times<TAB>
Out[67]:
In [68]:
norm(a) # the norm of the vector a
Out[68]:
Use the help() command for more info
In [69]:
help(dot)
In [70]:
?dot
Out[70]:
In [71]:
transpose(a) # this is a row vector
Out[71]:
In [72]:
a' # take the conjugate and then transpose
Out[72]:
In [73]:
a.'
# this just takes the transpose without the conjugate
Out[73]:
In [74]:
j = [5+9im, 4+2im, 8+3im]
Out[74]:
In [75]:
j'
Out[75]:
In [76]:
j.'
Out[76]:
To define a matrix use Matlab notation (with shape)
In [77]:
M = [2 1; 1 1]
Out[77]:
or the numpy
one by defining a list and reshaping:
In [78]:
N = reshape([1,3,4,4], (2,2))
Out[78]:
In [79]:
O = reshape(1:8, (2,2,2))
Out[79]:
In [80]:
transpose(M)
Out[80]:
In [81]:
inv(M) # inverse
Out[81]:
In [82]:
det(M) # determinant
Out[82]:
Indentation is not significant. The syntax is the same as in Python, but drop the colon, and add an end !
In [83]:
i = 0
while i < 5 print("$i\t")
i+=1
end
In [84]:
total = 0
for i = 1:10
total +=i
end
println("Sum is $total")
In [85]:
typeof(1:10)
Out[85]:
The notation 1:10
is a range object that can be iterated over!
You can construct an array by enclosing it in square brackets:
In [86]:
[1:2:10] # 1 to 10 by a step of 2
Out[86]:
In [87]:
[1:2:10, 19]
Out[87]:
In [88]:
a = 3
a < 5 && println("Small") # evaluate the second argument if the first is true
In [89]:
a>10 && println("Small")
Out[89]:
In [90]:
a>10 || println("Small") # this is semantics of the if_not-then
Similary the terciary operator:
In [91]:
a == 3 ? println("Hello") : println("Not true")
Just like in Python you can do array comprehensions.
In [92]:
squares = [i^2 for i in [1:2:10,7]]
Out[92]:
In [93]:
sums = [i+j for i=1:5, j=1:5]
Out[93]:
The notation with commas is a 1d vector (column vector).
In [94]:
v = [1,2,3]
Out[94]:
For a row vector, or matrices use the Matlab-style notation.
If we omit the commas we obtain a 1 x n matrix
In [95]:
row_vec = [3 4 5]
Out[95]:
Hermitian transpose : (Conjugate then transpose) by using '
Simple transpose : Transpose by using .'
In [96]:
row_vec = [3im 4 1+5im]
Out[96]:
In [97]:
row_vec'
Out[97]:
In [98]:
row_vec.'
Out[98]:
In [99]:
M = [1 2 ; 3 4]
Out[99]:
NB: There is a difference in the way Python and Julia treat slices of matrices
In Python: a one-dimensional slice in either direction returns a 1-dimensional vector.
In Julia: a vertical one dimensional slice gives a 1-dimensional vector ("column vector"), but a horizontal one-dimensional slice produces a 1xn matrix!
In [100]:
M[:,1]
Out[100]:
In [101]:
M[1,:]
Out[101]:
In [102]:
v = [1, 2]
Out[102]:
In [103]:
v*v
In [104]:
dot(v,v)
Out[104]:
In [105]:
M = [1 2 ; 3 4]
Out[105]:
In [106]:
M*v
Out[106]:
In [107]:
methods(*)
Out[107]:
In [108]:
N=[5 6 ; 9 8]
Out[108]:
In [109]:
M*N
Out[109]:
In [110]:
@which M*v # this tells me which exact function this uses
Out[110]:
This @which is a macro. It takes an expression and generates an expression.
In [111]:
rand() # Uniform in [0,1]
Out[111]:
In [112]:
x = rand(5)
Out[112]:
In [113]:
y = rand(5,5)
Out[113]:
For more distributions look at the Distributions.jl
In [114]:
M = rand(100,100)
Out[114]:
In [115]:
eig(M) # eigenvalues and eigenvectors
Out[115]:
In [116]:
typeof(ans)
Out[116]:
In [117]:
M2 = map(big, M) # `map` uses the function `big` over all elements of M
Out[117]:
In [118]:
lu(M2)
Out[118]:
In [119]:
methods(lu)
Out[119]:
In [120]:
@edit lu(M) # this is to open a text editor with the file
A julia script similar to Python is a sequence of commands placed on file with the suffix .jl.
From command-line, a script script.jl
is ran as:
$ julia script.jl arg1 arg2
where arg1
, arg2
are the command line arguments.
The command line arguments are placed in the variable ARGS as an array of strings.
In [121]:
outfile = open("test.txt", "w")
Out[121]:
In [122]:
for i in 1:10
println(outfile, "The value of i is $i") #send printl to file stream
end
close(outfile)
In [123]:
;cat test.txt # this is a shell command
In [124]:
infile = open("test.txt","r")
Out[124]:
In [125]:
lines = readlines(infile)
Out[125]:
In [126]:
map(split, lines)
Out[126]:
In [127]:
[float(line[6]) for line in map(split, lines)]
Out[127]:
In [128]:
x = rand(5)
Out[128]:
In [129]:
writedlm("rand5.txt", x)
In [130]:
;cat rand5.txt
In [131]:
y = rand(5,5)
Out[131]:
In [132]:
writedlm("rand55.txt",y)
In [133]:
;cat rand55.txt
Use readdlm
to read a matrix from the source where each line (separated by eol) gives one row, with elements separated by the given delimeter. The source can be a text file, stream or byte array. Memory mapped files can be used by passing the byte array representation of the mapped segment as source.
In [134]:
z = readdlm("rand55.txt")
Out[134]:
In [135]:
inv(z)
Out[135]:
In [136]:
;ls
or use the `command` and then run()
In [137]:
cmd=`ls -lt`
Out[137]:
In [138]:
run(cmd)
In [139]:
quad2(x) = x^2
Out[139]:
In [140]:
quad2(2)
Out[140]:
In [141]:
function quad(x)
x^2 # no explicit return needed
end
Out[141]:
The last value that is computed within the function is automatically returned, so no explicit return
statement is needed.
In [142]:
quad(2)
Out[142]:
Since every operator in Julia is a function and functions are implemented by specifying their action on different types something like this can work:
In [143]:
quad(2), quad(2.5), quad(1+3im), quad("hello")
Out[143]:
NB: String concat in Julia is done with the *
operator and not the +
operator. Repeating a string is thus done by raising it to an integer power.
So multiplying a string with a number will not work, but rainsing it to an integer power (i.e. multiplying it p times) will..
In [144]:
2*"hello"
In [145]:
"hello"^2
Out[145]:
In [146]:
s1 = "hello"
s2 = "Nikos"
Out[146]:
In [147]:
s1+s2
In [148]:
s1*s2
Out[148]:
But we can define the +
operator to work with string concat:
In [149]:
+(s1::String, s2::String) = string(s1, s2)
Out[149]:
In [150]:
"First"+" second"
Out[150]:
In [151]:
"Value is " + 3 # this is not yet defined for my +
A user-defined "composite type" is a colleciton of data. Unlike Python types do not "own" methods (functions internal to the type).
Methods are defined separately and are characterized by the types of all their arguments : multiple dispatch (dispatch is the process of choosing which "version" of a function to use).
For example, let's define a 2D vector type.
In [152]:
immutable Vector2D # we could also use type instead of immutable
x::Float64
y::Float64
end
We use the immutable
instead of type for efficiency: the object s stored in an efficient packed form.
In [153]:
Vector2D(3.0, 2.0)
Out[153]:
We then to define the operators ... For example for vector addition:
In [154]:
+(v::Vector2D, w::Vector2D) = Vector2D(v.x+w.x, v.y+w.y)
Out[154]:
In [155]:
v = Vector2D(3, 2)
Out[155]:
In [156]:
u = Vector2D(1,3)
Out[156]:
In [157]:
u+v
Out[157]:
In [158]:
import Base.show
In [159]:
show(io::IO, v::Vector2D) = print(io, "[$(v.x), $(v.y)]")
Out[159]:
In [160]:
v
Out[160]:
Types may have a parameter, for exampel:
In [161]:
immutable VectorND{T <: Real}
x::T
y::T
end
T
is a type parameter.
T <: Real
means that T
must be a subtype of the abstract type Real
.
We can investigate the hierarchy with the super()
function
In [162]:
super(Real)
Out[162]:
In [163]:
v = VectorND(3,4)
Out[163]:
In [164]:
n = VectorND(3+1im, 2) # this does not work since VectorND works only
# with Real values
In [165]:
type Particle
position::Vector2D
momentum::Vector2D
end
In [166]:
move(p::Particle, dt::Real) = p.position += p.momentum*dt
Out[166]:
In [167]:
show(io::IO, p::Particle) = print(io, "pos:$(p.position); vel: $(p.momentum)")
Out[167]:
In [168]:
+(v1::Vector2D, v2::Vector2D) = Vector2D(v1.x+v2.x, v1.y+v2.y)
Out[168]:
In [169]:
*(v1::Vector2D, lamb::Number) = Vector2D(lamb*v.x, lamb*v.y)
Out[169]:
In [170]:
p = Particle(Vector2D(0,0), Vector2D(1,1))
Out[170]:
In [171]:
move(p, 0.1)
Out[171]:
Now we can define a gas as a collection of particles...
In [172]:
type Gas
particles::Vector{Particle} # Array{Particle, 1}
function Gas(N::Int) # constructor
parts = [Particle(Vector2D(rand(2)...), Vector2D(rand(2)...)) for i in 1:N]
new(parts)
end
end
In [173]:
myg=Gas(10)
Out[173]:
In [174]:
function move(g::Gas, dt::Number)
for particle in g.particles
move(particle, dt)
end
end
Out[174]:
In [175]:
move(myg, 2)
In [176]:
myg
Out[176]:
Coming from Python the most accessible (and full-featured) package is PyPlot
, the Julia wrapper for pyplot
submodule of Matplotlib
.
To get it:
In [177]:
Pkg.add("PyPlot")
Pkg
is the package manager in Julia. It uses git repositories that are each cloned into their own subdirectory under the ~/.julia
directory.
The list of packages is available at Julia Packages
In [178]:
using PyPlot
This is similar to the Python syntax:
from <package> import *
and it makes all names in the packages available.
Instead of using PyPlot
one could use:
import PyPlot
in this case the names of the package will be available as PyPlot.plot()
etc
In [179]:
x = rand(10); y = rand(10);
In [180]:
p = plot(x,y, "ro-")
Out[180]:
In [181]:
p
Out[181]:
In [182]:
figure(figsize=(4,2))
Out[182]:
In [183]:
plot(rand(10),rand(10))
Out[183]:
In [184]:
L = 1000
Out[184]:
In [185]:
diffs = []
Out[185]:
In [186]:
for i in 1:10
M = randn(L, L)
M = Symmetric(M)
lamb = eigvals(M)
diffs = [diffs, diff(lamb)]
end
In [187]:
diffs
Out[187]:
In [188]:
h = plt[:hist](diffs, 300) #weird syntax
Out[188]:
For more examples of plots using PyPlot check:
In [200]:
using Gadfly
In [201]:
xs = 1:10
Out[201]:
In [202]:
ys = rand(10)
Out[202]:
In [203]:
Gadfly.plot(x=xs, y=ys)
Out[203]:
The plot can be panned and moved interactively.
In [204]:
Gadfly.plot(x=xs, y=ys, Geom.line) # adding a Geometry
Out[204]:
In [205]:
Gadfly.plot(x=xs, y=ys, Geom.line, Geom.point)
Out[205]:
In [206]:
Gadfly.plot(x=rand(10), y=rand(10), Geom.line)
Out[206]:
... is not really random at all.
Thus to preserve the order we can use the argument preserve_order=true.
In [207]:
Gadfly.plot(x=rand(10), y=rand(10), Geom.point, Geom.line(preserve_order=true))
Out[207]:
In [208]:
p=Gadfly.plot(layer(x=rand(10), y=rand(10), Geom.point, Geom.line(preserve_order=true)),
layer(x=rand(10), y=rand(10), Geom.point, Geom.line(preserve_order=true)), Guide.XLabel("first"), Guide.YLabel("second"))
Out[208]:
Guide is the driver for the information about the plot. Guide.XLabel, Guide.YLabel put the labels into axes.
We can also write directly to PDF:
In [209]:
#Gadfly.draw(PDF("stuff.pdf", 10cm, 5cm), p) # this is fucked up atm
In [210]:
#;open stuff.pdf
In [211]:
using RDatasets
In [212]:
irises = dataset("datasets", "iris")
Out[212]:
In [213]:
head(irises)
Out[213]:
In [214]:
Gadfly.plot(irises, x="SepalLength",y="SepalWidth", Geom.point)
Out[214]:
In [216]:
Gadfly.plot(irises, x="SepalLength",y="SepalWidth", color="Species", Geom.point)
Out[216]:
To be added (look at SciPy 2014)
When profiling run each function once with the correct argument types before timing it, since the first time is run the compilation time will play a large role!
In [222]:
@time cos(10) # run it 2 times ;)
Out[222]:
Detailed profile can be done with the @profile macro
In [245]:
@profile cos(10)
Out[245]:
A package named ProfileView.jl gives you a graphical profiling.
In [224]:
function sum1(N::Int)
total = 0 # total is defined as Integer
for i in 1:N
total +=i/2
end
total
end
function sum2(N::Int)
total = 0.0 # here total is defined as float from the start
for i in 1:N
total +=i/2
end
total
end
Out[224]:
In [226]:
sum1(10), sum2(10)
Out[226]:
In [227]:
N=1000000
Out[227]:
In [229]:
@time sum1(N)
Out[229]:
In [230]:
@time sum2(N)
Out[230]:
They produce the same result, but the time is different.
There are 4 functions that can access every step of the compilation process
In [235]:
code_lowered(sum1, (Int,)) # name of func, tuple of tupes of arguments
Out[235]:
In [234]:
code_lowered(sum2, (Int,))
Out[234]:
Due to the type stability the two code_lowered are different
In [238]:
code_typed(sum1, (Int,)) # check the Union in the result
Out[238]:
In [239]:
code_typed(sum2, (Int,))
Out[239]:
In [240]:
code_llvm(sum1, (Int,))
In [242]:
code_native(sum1, (Int,)) # assembly
In [246]:
using PyCall
PyCall has a high-level intereface that transports between Julia and Python trasparently from the users point of view.
For example let's call Python's math
module!
In [247]:
@pyimport math
In [248]:
math.sin(math.pi)
Out[248]:
Array objects are automatically converted.. So for numpy
In [249]:
@pyimport numpy.random as nprandom
In [251]:
nr=nprandom.rand(3,4) # this returns a ndarray... but in juliaaa...
Out[251]:
In [253]:
typeof(nr) # but within Julia this is an array
Out[253]:
Defining a Julia function
In [254]:
objective = x -> cos(x) - x # this is a Julia anonymous function
Out[254]:
In [255]:
objective(3)
Out[255]:
We can also pass this Julia (anonymous) function to a Python module:
In [256]:
@pyimport scipy.optimize as so
In [257]:
so.newton(objective,1)
Out[257]:
Julia has ODE solvers in ODE.jl
and Sundials.jl
, but we can also use Python solvers.
In [259]:
@pyimport scipy.integrate as integrate
f(x,t)= -x
Out[259]:
In [261]:
t=[0:0.1:10];
In [262]:
soln = integrate.odeint(f,1,t)
Out[262]:
Note that PyPlot package provides a higher-level wrapper than PyCall around matplotlib
In [264]:
plot(t, soln)
Out[264]:
In Python accessing fields and properties of objects is done using the
obj.a and obj.b()
syntax. However, the syntax obj.b
in Julia is restricted to accessing fields of composite types.
Thus to access fields and methods of Python objects via PyCall, it is necessary to use the syntax:
For Fields: obj[:a]
For Methods: obj[:a]()
Where here, we use the Julia syntax :a
to mean the Julia symbol a
.
The PyCall interface is built ontop of the interface which transports objects between Python and Julia: PyObject
, which wraps the PyObject*
in C and represents a reference to a Python object
In [265]:
PyObject(3)
Out[265]:
In [268]:
xx = PyObject(rand(5,5))
Out[268]:
In [269]:
typeof(xx)
Out[269]:
In [270]:
names(xx)
Out[270]:
In [271]:
xx.o
Out[271]:
In [272]:
#xx.shape in python becomes
xx[:shape]
Out[272]:
NB: Julia arrays passed in Python without a copy, but by default Python array is copied when a result is requested in Julia. This can be avoided at a lower level using pycall
and PyArray
.
Within Julia you can call C and Fortran functions in shared libraries, via the ccall
function.
To use it we need to specify :
and the name of the shared library; given as an ordered pair (tuple)
the return type of the function
An example using the clock function:
In [274]:
t = ccall( (:clock, "libc"), Int32, ())
Out[274]:
In [275]:
typeof(t)
Out[275]:
In [277]:
path = ccall( (:getenv, "libc"), Ptr{UInt8}, (Ptr{UInt8},), "PATH")
# (name of function, library), return type, (argument type,), argument)
# Ptr{UInt8} is a pointer to the Uint8 type
Out[277]:
In [278]:
path
Out[278]:
In [279]:
bytestring(path) # create a string from the address of a C string
Out[279]:
In [289]:
:s
Out[289]:
In [290]:
typeof(:s)
Out[290]:
:s
refers to the symbol s
and we can evaluate with the eval
function
In [291]:
eval(:s) # not yet defined..
In [292]:
s=3; eval(:s)
Out[292]:
The eval
function takes an expression and evaluates it, generating the corresponding code.
Everything is a symbol:
In [293]:
:+, :sin
Out[293]:
and symbols can be combined into expressions, that are the basic objects which represent pieces of Julia code.
In [295]:
ex = :(a+b) # this is the expression a+b
Out[295]:
In [296]:
typeof(ex)
Out[296]:
Expressions are Julia's objects, so we can find more info about it
In [297]:
names(ex)
Out[297]:
In [299]:
ex.args # ex.<TAB>
Out[299]:
In [302]:
ex.args[2]
Out[302]:
But expressions can be arbitrary Julia code, that when evaluated will have side effects.
For longer blocks of code quote ... end may be used instead of :(...)
In [303]:
ex2 =
quote
y=3
z=sin(y+1)
end
Out[303]:
In [304]:
eval(ex2)
Out[304]:
In [306]:
Meta.show_sexpr(ex2) # this is to show the full info
In [307]:
dump(ex2) # to dump the internal of an expression
Using this idea we can write Julia code on the fly from within Julia : programming a program!
The macro
name is given to a kind of 'super-function' that takes a piece of code as an argument and returns an altered piece of code.
Macros map a tuple of argument expressions to a returned expression.
Macros are useful to
Macros are invoked using the @
sign.
For example:
In [308]:
@time sin(10)
Out[308]:
We can defin a macro as follows. The $ sign is used to interpolate the valie of the expression (as in string interpolation)
In [309]:
macro duplicate(ex)
quote
$ex
$ex
end
end
In [310]:
@duplicate println(sin(10))
In [315]:
ex = :(@duplicate println(sin(10))) #to understand what it does...
Out[315]:
In [313]:
eval(ex)
In [314]:
macroexpand(ex)
Out[314]:
In [316]:
macroexpand(:(@time sine(10)))
Out[316]:
In [ ]: