List<String>
struct FreePoint{T,S}
x::T
y::S
end
p1 = FreePoint(2.5, 3)
FreePoint{Float64,Int64}(2.5, 3)
typeof(p1)
FreePoint{Float64,Int64}
Restrict types to be the same.
struct Point{T}
x::T
y::T
end
p1 = Point(3, 4)
Point{Int64}(3, 4)
typeof(p1)
Point{Int64}
p2 = Point(3., 4.)
Point{Float64}(3.0, 4.0)
typeof(p2)
Point{Float64}
typeof(p1) == typeof(p2)
false
Point(3, 4.)
MethodError: no method matching Point(::Int64, ::Float64) Closest candidates are: Point(::T, !Matched::T) where T at In[4]:2 Stacktrace: [1] top-level scope at In[10]:1
Point("abc", "λ")
Point{String}("abc", "λ")
Restrict types should be the subtypes of Real
.
struct Point2{T<:Real}
x::T
y::T
end
Point(3, 4)
Point{Int64}(3, 4)
Point2("3", "4")
MethodError: no method matching Point2(::String, ::String) Stacktrace: [1] top-level scope at In[14]:1
function sum(p1::Point, p2::Point)
Point(p1.x + p2.x, p1.y + p2.y)
end
sum (generic function with 1 method)
p1
Point{Int64}(3, 4)
p2
Point{Float64}(3.0, 4.0)
sum(p1, p2)
Point{Float64}(6.0, 8.0)
Matrix{Int64}(undef, 8, 8)
8×8 Array{Int64,2}: 139719740428400 139719740428912 … 139719740431472 139719740431984 139719740428464 139719740428976 139719740431536 139719740432048 139719740428528 139719740429040 139719740431600 139719740432112 139719740428592 139719740429104 139719740431664 139719740432176 139719818970736 139719740429168 139719740431728 139719740432240 139719740428720 139719740429232 … 139719740431792 139719740432304 139719740428784 139719740429296 139719740431856 139719740432368 139719740428848 139719740429360 139719740431920 139719740432432
To store integer as a property of type.
struct Point3{T}
x
y
end
Point3{2}(3, 4)
Point3{2}(3, 4)
Restrict argument types with parameters.
function foo(p1::Point2{T}, p2::Point2{T}) where {T}
Point(p1.x + p2.x, p1.y + p2.y)
end
foo (generic function with 1 method)
p1 = Point2(3, 4)
p2 = Point2(3., 4.)
foo(p1, p2)
MethodError: no method matching foo(::Point2{Int64}, ::Point2{Float64}) Closest candidates are: foo(::Point2{T}, !Matched::Point2{T}) where T at In[22]:2 Stacktrace: [1] top-level scope at In[23]:3
p3 = Point2(3., 4.)
foo(p3, p2)
Point{Float64}(6.0, 8.0)
same_type(x::T, y::T) where {T} = true
same_type(x, y) = false
same_type (generic function with 2 methods)
same_type(1, 2) # the same type
true
same_type(1, 2.0) # distinct type
false
concat(v::Vector{T}, x::T) where {T} = [v..., x]
concat (generic function with 1 method)
concat([1, 2, 3], 4)
4-element Array{Int64,1}: 1 2 3 4
concat([1, 2, 3], 4.0)
MethodError: no method matching concat(::Array{Int64,1}, ::Float64) Closest candidates are: concat(::Array{T,1}, !Matched::T) where T at In[28]:1 Stacktrace: [1] top-level scope at In[30]:1
foobar(a, b, x::T) where {T <: Integer} = (a, b, x)
foobar (generic function with 1 method)
foobar(1, 2, 3)
(1, 2, 3)
foobar(1, 2.0, 3)
(1, 2.0, 3)
foobar(1, 2.0, 3.0)
MethodError: no method matching foobar(::Int64, ::Float64, ::Float64) Closest candidates are: foobar(::Any, ::Any, !Matched::T) where T<:Integer at In[31]:1 Stacktrace: [1] top-level scope at In[34]:1
Parametric method can help us extract types from parametric types.
Base.eltype(::Point{T}) where {T} = T
is equivalent to
Base.eltype(p::Point{T}) where {T} = T
p1 = Point(3., 4.)
eltype(p1)
Float64
In Julia, types are themselves objects. You can do anything what an object can do for them.
function Base.zero(p::Point{T}) where {T}
Point(T(0), T(0))
end
zero(p1)
Point{Float64}(0.0, 0.0)
zero(Point(3, 4))
Point{Int64}(0, 0)
To instantiate a parametric type, a constructor is needed.
struct Point{T<:Real}
x::T
y::T
end
is equivalent to
type Point{T<:Real}
x::T
y::T
Point{T}(x,y) where {T<:Real} = new(x,y)
end
Point(x::T, y::T) where {T<:Real} = Point{T}(x,y)
Parametric type is provided in Point(x::T, y::T)
implicitly.
Point(1,2)
Point{Int64}(1, 2)
Point(1.0,2.5)
Point{Float64}(1.0, 2.5)
Point(1,2.5)
MethodError: no method matching Point(::Int64, ::Float64) Closest candidates are: Point(::T, !Matched::T) where T<:Real at In[1]:2 Stacktrace: [1] top-level scope at In[4]:1
Parametric type is provided in Point{T}(x,y)
explicity. Thus, you may want to cast values by specifing types.
Point{Int64}(1, 2)
Point{Int64}(1, 2)
Point{Int64}(1.0, 2.5)
InexactError: Int64(2.5) Stacktrace: [1] Int64 at ./float.jl:710 [inlined] [2] convert at ./number.jl:7 [inlined] [3] Point{Int64}(::Float64, ::Float64) at ./In[1]:2 [4] top-level scope at In[6]:1
Point{Float64}(1.0, 2.5)
Point{Float64}(1.0, 2.5)
Point{Float64}(1, 2)
Point{Float64}(1.0, 2.0)
abstract type Animal end
abstract type Dog <: Animal end
abstract type Cat <: Animal end
struct Labrador <: Dog
end
struct GoldenRetriever <: Dog
end
isanimal(::T) where {T <: Animal} = true
isanimal(x) = false
isanimal (generic function with 2 methods)
isanimal(Labrador())
true
isanimal(GoldenRetriever())
true
isanimal(0)
false
concat(v::Vector{T}, x::T) where {T} = [v..., x]
concat (generic function with 1 method)
concat([1,2,3], 4)
4-element Array{Int64,1}: 1 2 3 4
concat([1,2,3], 4.)
MethodError: no method matching concat(::Array{Int64,1}, ::Float64) Closest candidates are: concat(::Array{T,1}, !Matched::T) where T at In[14]:1 Stacktrace: [1] top-level scope at In[16]:1
Suppose you have some data to be flatten...
xs = [["a", "b", "c"], "d", "e", ["f", "g"]];
collections = []
for x in xs
if x isa String
push!(collections, x)
elseif x isa Vector
for i in x
push!(collections, i)
end
end
end
collections
7-element Array{Any,1}: "a" "b" "c" "d" "e" "f" "g"
flatten!(collections, x::String) = (push!(collections, x))
function flatten!(collections, x::Vector)
for i in x
push!(collections, i)
end
end
flatten! (generic function with 2 methods)
collections = []
for x in xs
flatten!(collections, x)
end
collections
7-element Array{Any,1}: "a" "b" "c" "d" "e" "f" "g"
Sometimes, you want to define an interface for your method, instead of implementation. It is usually used in a framework to provide features which are compatible to user-defined methods.
function generic # no arguments, as a placeholder
end
generic (generic function with 0 methods)
methods(generic)
generic()
MethodError: no method matching generic() Stacktrace: [1] top-level scope at In[25]:1
abstract type Animal end
struct Dog <: Animal
color::String
species::String
end
struct Cat <: Animal
color::String
species::String
end
function color(a::Animal)
return a.color
end
function voice(d::Dog)
return "bark"
end
function voice(c::Cat)
return "meow"
end
voice (generic function with 2 methods)
d1 = Dog("yellow", "Labrador")
Dog("yellow", "Labrador")
voice(d1)
"bark"
c1 = Cat("brown", "?")
Cat("brown", "?")
voice(c1)
"meow"
Simple point of service system (POS)
abstract type Item end
struct OrderList{T<:Item}
item_list::Vector{T}
OrderList{T}() where {T<:Item} = new(Item[])
end
struct Apple <: Item
price
Apple() = new(100)
end
struct Banana <: Item
price
Banana() = new(50)
end
add!(ol::OrderList, it::Item) = push!(ol.item_list, it)
function Base.sum(ol::OrderList)
s = 0
for it in ol.item_list
s += it.price
end
return s
end
l = OrderList{Item}()
OrderList{Item}(Item[])
add!(l, Apple())
1-element Array{Item,1}: Apple(100)
add!(l, Apple())
add!(l, Apple())
add!(l, Banana())
add!(l, Banana())
5-element Array{Item,1}: Apple(100) Apple(100) Apple(100) Banana(50) Banana(50)
sum(l)
400
l = OrderList{Apple}()
OrderList{Apple}(Apple[])
add!(l, Apple())
add!(l, Apple())
2-element Array{Apple,1}: Apple(100) Apple(100)
add!(l, Banana())
MethodError: Cannot `convert` an object of type Banana to an object of type Apple Closest candidates are: convert(::Type{T}, !Matched::T) where T at essentials.jl:171 Stacktrace: [1] push!(::Array{Apple,1}, ::Banana) at ./array.jl:913 [2] add!(::OrderList{Apple}, ::Banana) at ./In[9]:1 [3] top-level scope at In[16]:1
sum(l)
200
function correlation(x, y)
n = length(x)
@assert length(y) == n "Not matched sample size"
x̄ = sum(x) / n
ȳ = sum(y) / n
x̂ = x .- x̄
ŷ = y .- ȳ
ρ = sum(x̂ .* ŷ) / (sqrt(sum(x̂.^2))*sqrt(sum(ŷ.^2)))
return ρ
end
correlation (generic function with 1 method)
x = [2, 3, 4, 5, 6, 2, 3, 4, 5]
y = [32, 32, 5, 42, 6, 17, 19, 20, 24];
correlation(x, y)
-0.20034374130204088