That depends on your use case:
Julia has all of the basic types and functions you need to do math
# Integers
1
1
# Floating point numbers
1.0
1.0
# Strings
"a"
"a"
# Complex numbers
1 + 2im
1 + 2im
# Vectors
[1, 2, 3]
3-element Array{Int64,1}: 1 2 3
# Matrices
[1.0 2.0; 3.1 4.2]
2x2 Array{Float64,2}: 1.0 2.0 3.1 4.2
# Elementary functions
sin(1.0)
0.8414709848078965
subtypes(Any)[1:4]
4-element Array{Any,1}: AbstractArray{T,N} AbstractCmd AbstractRNG Algorithm
subtypes(Number)
5-element Array{Any,1}: Complex{Float16} Complex{Float32} Complex{Float64} Complex{T<:Real} Real
subtypes(Real)
4-element Array{Any,1}: FloatingPoint Integer MathConst{sym} Rational{T<:Integer}
subtypes(Integer)
5-element Array{Any,1}: BigInt Bool Char Signed Unsigned
subtypes(None)
no method subtypes(Type{None}) while loading In[12], in expression starting on line 1
sqrt(2)
1.4142135623730951
sqrt(2.0)
1.4142135623730951
1 + 1
2
1.0 + 1.0
2.0
"wat" + 1
no method +(ASCIIString, Int64) while loading In[5], in expression starting on line 1
1 + "wat"
no method +(Int64, ASCIIString) while loading In[6], in expression starting on line 1
"wat" - 1
no method -(ASCIIString, Int64) while loading In[7], in expression starting on line 1
1 - "wat"
no method -(Int64, ASCIIString) while loading In[8], in expression starting on line 1
The meaning of a function can depend upon the types of its inputs
function foo(a, b)
return a + b
end
foo (generic function with 1 method)
foo(1, 2)
3
foo(1.0, 2)
3.0
bar(a, b) = a * b
bar (generic function with 1 method)
bar(2, 3)
6
bar(1, "wat")
no method *(Int64, ASCIIString) while loading In[14], in expression starting on line 1 in bar at In[12]:1
bar("NaN", "NaN")
"NaNNaN"
folly(a::Integer, b::Integer) = 1
folly (generic function with 1 method)
folly(a::Float64, b::Float64) = 2
folly (generic function with 2 methods)
folly(1, 2)
1
folly(1.0, 2.0)
2
type NewType
a::Int
b::UTF8String
end
x = NewType(1, "this is a string")
NewType(1,"this is a string")
x.a, x.b
(1,"this is a string")
module NewNamespace
c = 42
export c
end
c
c not defined while loading In[24], in expression starting on line 1
using NewNamespace
c
42
c = 41
41
Warning: imported binding for c overwritten in module Main
NewNamespace.c
42
If Julia looks like most dynamic languages, why is it so fast?
Julia generates custom function implementations for every input type signatures
foo(a, b) = a + b
foo (generic function with 1 method)
code_llvm(foo, (Int, Int))
define i64 @julia_foo1292(i64, i64) { top: %2 = add i64 %1, %0, !dbg !3947 ret i64 %2, !dbg !3947 }
code_llvm(foo, (Float64, Float64))
define double @julia_foo1305(double, double) { top: %2 = fadd double %0, %1, !dbg !3986 ret double %2, !dbg !3986 }
code_native(foo, (Int, Int))
.section __TEXT,__text,regular,pure_instructions Filename: In[29] Source line: 1 push RBP mov RBP, RSP Source line: 1 add RDI, RSI mov RAX, RDI pop RBP ret
code_native(foo, (Float64, Float64))
.section __TEXT,__text,regular,pure_instructions Filename: In[29] Source line: 1 push RBP mov RBP, RSP Source line: 1 vaddsd XMM0, XMM0, XMM1 pop RBP ret
For simple functions, Julia's specialized implementation is similar to a natural C implementation
As Julia gets smarter, its static analyses tools improve all code you write:
function foo()
r = 0
for i in 1:12
r += 2
end
return r
end
code_llvm(foo, ())
define i64 @julia_foo1307() { pass2: ret i64 24, !dbg !3993 }
To make static analysis effective, Julia rules out certain types of code:
R's reification of scope is ruled out by Julia's design:
f1 <- function(scope.number) {
ls(env = sys.frame(scope.number))
}
f2 <- function() {
a <- 1
my.scope <- sys.nframe()
f1(my.scope)
}
f2()
R's ability to mutate ad hoc scopes is ruled out by Julia's design:
g1 <- function(var.name, scope.number)
{
assign("a", -1, envir = sys.frame(scope.number))
}
g2 <- function()
{
a <- 1
scope.number <- sys.nframe()
print(a)
g1("a", scope.number)
print(a)
}
g2()
R's type mutation by assignment is ruled out by Julia's design:
v <- c(1, 2, 3)
v[1] <- "a"
print(v)
To make defaults efficient, Julia assumes you want to work close to the metal:
function fib{T <: Integer}(n::T)
if n == zero(T)
return zero(T)
elseif n == one(T)
return one(T)
else
a, b = zero(T), one(T)
i = 1
while i < n
a, b = b, a + b
i += 1
end
return b
end
end
fib (generic function with 1 method)
fib(95)
-4953053512429003327
fib(BigInt(95))
31940434634990099905
using StatsBase
modes([1, 1, 2, 2, 3])
2-element Array{Int64,1}: 2 1
corspearman(rand(100), rand(100))
-0.08795679567956798
using DataArrays
da = @data([1, 2, NA, 4])
4-element DataArray{Int64,1}: 1 2 NA 4
using DataFrames
df = DataFrame(
A = [1, 2, 3],
B = ["a", "b", "c"],
C = [1//2, 3//4, 5//6]
)
A | B | C | |
---|---|---|---|
1 | 1 | a | 1//2 |
2 | 2 | b | 3//4 |
3 | 3 | c | 5//6 |
using Distributions
srand(1)
x = rand(Normal(10, 1), 10)
10-element Array{Float64,1}: 10.6701 10.5509 9.93663 11.3369 9.92685 9.25454 8.77994 9.94682 9.83486 7.88463
pdf(Normal(10, 1), 1.0)
1.0279773571668915e-18
loglikelihood(Normal(10, 1), x)
-13.738557913500014
using RDatasets
iris = dataset("datasets", "iris")
SepalLength | SepalWidth | PetalLength | PetalWidth | Species | |
---|---|---|---|---|---|
1 | 5.1 | 3.5 | 1.4 | 0.2 | setosa |
2 | 4.9 | 3.0 | 1.4 | 0.2 | setosa |
3 | 4.7 | 3.2 | 1.3 | 0.2 | setosa |
4 | 4.6 | 3.1 | 1.5 | 0.2 | setosa |
5 | 5.0 | 3.6 | 1.4 | 0.2 | setosa |
6 | 5.4 | 3.9 | 1.7 | 0.4 | setosa |
7 | 4.6 | 3.4 | 1.4 | 0.3 | setosa |
8 | 5.0 | 3.4 | 1.5 | 0.2 | setosa |
9 | 4.4 | 2.9 | 1.4 | 0.2 | setosa |
10 | 4.9 | 3.1 | 1.5 | 0.1 | setosa |
11 | 5.4 | 3.7 | 1.5 | 0.2 | setosa |
12 | 4.8 | 3.4 | 1.6 | 0.2 | setosa |
13 | 4.8 | 3.0 | 1.4 | 0.1 | setosa |
14 | 4.3 | 3.0 | 1.1 | 0.1 | setosa |
15 | 5.8 | 4.0 | 1.2 | 0.2 | setosa |
16 | 5.7 | 4.4 | 1.5 | 0.4 | setosa |
17 | 5.4 | 3.9 | 1.3 | 0.4 | setosa |
18 | 5.1 | 3.5 | 1.4 | 0.3 | setosa |
19 | 5.7 | 3.8 | 1.7 | 0.3 | setosa |
20 | 5.1 | 3.8 | 1.5 | 0.3 | setosa |
21 | 5.4 | 3.4 | 1.7 | 0.2 | setosa |
22 | 5.1 | 3.7 | 1.5 | 0.4 | setosa |
23 | 4.6 | 3.6 | 1.0 | 0.2 | setosa |
24 | 5.1 | 3.3 | 1.7 | 0.5 | setosa |
25 | 4.8 | 3.4 | 1.9 | 0.2 | setosa |
26 | 5.0 | 3.0 | 1.6 | 0.2 | setosa |
27 | 5.0 | 3.4 | 1.6 | 0.4 | setosa |
28 | 5.2 | 3.5 | 1.5 | 0.2 | setosa |
29 | 5.2 | 3.4 | 1.4 | 0.2 | setosa |
30 | 4.7 | 3.2 | 1.6 | 0.2 | setosa |
⋮ | ⋮ | ⋮ | ⋮ | ⋮ | ⋮ |
using Optim
x = rand(Normal(31, 11), 1_000)
1000-element Array{Float64,1}: 22.3331 41.9854 25.5507 35.0022 38.4288 37.6098 14.4765 26.8999 16.957 38.0962 51.9647 13.5306 34.2712 ⋮ 15.7977 21.9261 23.4996 40.0516 27.46 36.5742 33.2723 21.4413 31.422 39.2109 24.5928 39.204
function nll(theta)
-loglikelihood(Normal(theta[1], exp(theta[2])), x)
end
nll (generic function with 1 method)
nll([1.0, 1.0])
70556.9217598127
fit = optimize(nll, [0.0, 0.0])
Results of Optimization Algorithm * Algorithm: Nelder-Mead * Starting Point: [0.0,0.0] * Minimum: [30.87859403957906,2.4004058957100334] * Value of Function at Minimum: 3819.342967 * Iterations: 54 * Convergence: true * |x - x'| < NaN: false * |f(x) - f(x')| / |f(x)| < 1.0e-08: true * |g(x)| < NaN: false * Exceeded Maximum Number of Iterations: false * Objective Function Calls: 106 * Gradient Call: 0
theta = fit.minimum
2-element Array{Float64,1}: 30.8786 2.40041
Normal(theta[1], exp(theta[2]))
Normal( μ=30.87859403957906 σ=11.027651548809786 )