In [2]:
# C signature:
# char *getenv(const char *name)
#
# Julia syntax:
# ccall(function, return_type, (input_type, intput_type, ...), input_value, input_value, ...)
#
path = ccall(:getenv, Ptr{Uint8}, (Ptr{Uint8},), "SHELL")
bytestring(path)
Out[2]:
In [3]:
# call from any shared library (.so, .dylib, .dll)
mysin(x::Any) = ccall((:sin,"libm"), Cdouble, (Cdouble,), x)
Out[3]:
In [4]:
mysin(3.0)
Out[4]:
In [5]:
# ccall automatically converts the types (if possible)
mysin(3)
Out[5]:
In [6]:
code_native(mysin, (Float64,))
In [7]:
# The standard library uses ccall as well
@which sin(3.)
Out[7]:
In [8]:
# Code can be "vectorized" on the Julia side.
mysin(x::Array{Float64, 1}) = [mysin(xi) for xi in x]
Out[8]:
In [10]:
mysin("hi")
In [11]:
# There are macros to reduce boilerplate
@vectorize_1arg Real mysin
Out[11]:
In [12]:
methods(mysin)
Out[12]:
In [13]:
using PyCall
In [ ]:
# you've got numpy
@pyimport numpy as np
x = [-100, 39, 59, 55, 20]
np.cumsum(x)
In [14]:
# you've got scipy
@pyimport scipy.optimize as so
function f(x)
println(" calling f($x)")
cos(x) - x
end
so.newton(f, 1.2)
Out[14]:
In [ ]:
# even matplotlib
using PyPlot
In [ ]:
x = linspace(0,2π,1000)
fig = plot(x, sin(3x + cos(5x)), "b--")
title("a funny plot")
fig = PyPlot.gcf()
Macros are functions operate on expressions (code) rather than values: whereas a function takes input value(s), say 3
and returns some output value, say 9, a macro takes input expression(s), say x
and returns an output expression, say x^2
.
One might also say that macros rewrite or generate code.
Here is an example of why we might want to do this:
In [1]:
x = rand(5)
# suppose we want to time an element-wise square
t1 = time_ns()
x.^2
t2 = time_ns()
println(t2-t1, " nanoseconds")
In [ ]:
@time x.^2
In [ ]:
macroexpand(:(@time x.^2))