04: Funkce a metody, makra a metaprogramování
Tento notebook je výukovým materiálem v předmětu BI-JUL.21 vyučovaném v zimním semestru akademického roku 2023/2024 Tomášem Kalvodou. Tvorba těchto materiálů byla podpořena NVS FIT.
Hlavní stránkou předmětu, kde jsou i další notebooky a zajímavé informace, je jeho Course Pages stránka.
versioninfo()
1. Funkce a metody
V předchozí kapitole jsme si ukázali, jak se v Julia pracuje s typy. Asi jste si všimli, že na Julia typy a jejich hierarchii se lze dívat trochu jako na třídy v jiných programovacích jazycích.
Na rozdíl ale třeba od C++, Javy, Ruby, nebo Pythonu, nejsou instance tříd (objekty) vybaveny metodami. Koncept funkcí a metod je v Julia prakticky zcela oddělen od typů.
1.1 Multiple dispatch
V matematice se jeden symbol používá k označení různých binárních operací. Všechny tyto operace mají jisté společné vlastnosti, které umožňují dívat se na danou operaci jako na sčítání. Tento abstraktní koncept sčítání by v Julia odpovídal funkci. Různé konkrétní způsoby sčítání objektů (nejen) stejných typů (čísla, matice,...) pak z pohledu Julia odpovídají metodám.
Jinak řečeno, Julia funkce pod jedním společným jménem sdružuje více metod, které se při volání použijí v závislosti na typech argumentů. Toto paradigma se označuje multiple dispatch (v kontrastu k single dispatch, kde volání je vázáno na konkrétní třídu).
Například zmíněná funkce +
má v Julia následující metody (lehce extrémní příklad, ale názorný):
methods(+)
- +(x::T, y::T) where T<:Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8} in Base at int.jl:87
- +(x::T, y::T) where T<:Union{Float16, Float32, Float64} in Base at float.jl:408
- +(c::Union{UInt16, UInt32, UInt64, UInt8}, x::BigInt) in Base.GMP at gmp.jl:539
- +(c::Union{Int16, Int32, Int64, Int8}, x::BigInt) in Base.GMP at gmp.jl:545
- +(c::Union{UInt16, UInt32, UInt64, UInt8}, x::BigFloat) in Base.MPFR at mpfr.jl:407
- +(c::Union{Int16, Int32, Int64, Int8}, x::BigFloat) in Base.MPFR at mpfr.jl:415
- +(c::Union{Float16, Float32, Float64}, x::BigFloat) in Base.MPFR at mpfr.jl:423
- +(x::Union{Dates.CompoundPeriod, Dates.Period}) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/periods.jl:342
- +(F::LinearAlgebra.Hessenberg, J::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/hessenberg.jl:647
- +(r::AbstractRange{<:Dates.TimeType}, x::Dates.Period) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/ranges.jl:65
- +(x::AbstractArray{<:Dates.TimeType}, y::Union{Dates.CompoundPeriod, Dates.Period}) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/deprecated.jl:6
- +(A::LinearAlgebra.Tridiagonal, B::LinearAlgebra.Tridiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/tridiag.jl:754
- +(x::LinearAlgebra.Tridiagonal, H::LinearAlgebra.UpperHessenberg) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/hessenberg.jl:112
- +(A::LinearAlgebra.Tridiagonal, B::LinearAlgebra.SymTridiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:162
- +(A::LinearAlgebra.Tridiagonal, B::LinearAlgebra.Diagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:178
- +(A::LinearAlgebra.Tridiagonal, B::LinearAlgebra.Bidiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:198
- +(A::LinearAlgebra.Tridiagonal{var"#s972", V} where {var"#s972"<:Number, V<:AbstractVector{var"#s972"}}, B::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:231
- +(A::SparseArrays.AbstractSparseMatrix, B::LinearAlgebra.Hermitian{<:Any, <:SparseArrays.AbstractSparseMatrix}) in SparseArrays at /usr/share/julia/stdlib/v1.9/SparseArrays/src/linalg.jl:14
- +(A::SparseArrays.AbstractSparseMatrix, B::LinearAlgebra.Hermitian) in SparseArrays at /usr/share/julia/stdlib/v1.9/SparseArrays/src/linalg.jl:17
- +(A::SparseArrays.AbstractSparseMatrix, B::LinearAlgebra.Symmetric{<:Any, <:SparseArrays.AbstractSparseMatrix}) in SparseArrays at /usr/share/julia/stdlib/v1.9/SparseArrays/src/linalg.jl:14
- +(A::SparseArrays.AbstractSparseMatrix, B::LinearAlgebra.Symmetric) in SparseArrays at /usr/share/julia/stdlib/v1.9/SparseArrays/src/linalg.jl:17
- +(x::P, y::P) where P<:Dates.Period in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/periods.jl:75
- +(x::Dates.Period, y::Dates.Period) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/periods.jl:331
- +(y::Dates.Period, x::Dates.CompoundPeriod) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/periods.jl:333
- +(y::Dates.Period, x::Dates.TimeType) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/arithmetic.jl:85
- +(x::Dates.Period, r::AbstractRange{<:Dates.TimeType}) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/ranges.jl:64
- +(y::Union{Dates.CompoundPeriod, Dates.Period}, x::AbstractArray{<:Dates.TimeType}) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/deprecated.jl:14
- +(A::LinearAlgebra.UnitUpperTriangular, B::LinearAlgebra.UpperTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/triangular.jl:646
- +(A::LinearAlgebra.UnitUpperTriangular, B::LinearAlgebra.UnitUpperTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/triangular.jl:648
- +(UL::LinearAlgebra.UnitUpperTriangular, J::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/uniformscaling.jl:181
- +(x::LinearAlgebra.UnitUpperTriangular, H::LinearAlgebra.UpperHessenberg) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/hessenberg.jl:112
- +(A::LinearAlgebra.UnitUpperTriangular, B::LinearAlgebra.Bidiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:91
- +(A::LinearAlgebra.Bidiagonal, B::LinearAlgebra.Bidiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/bidiag.jl:374
- +(x::LinearAlgebra.Bidiagonal, H::LinearAlgebra.UpperHessenberg) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/hessenberg.jl:112
- +(A::LinearAlgebra.Bidiagonal, B::LinearAlgebra.UpperTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:99
- +(A::LinearAlgebra.Bidiagonal, B::LinearAlgebra.UnitUpperTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:99
- +(A::LinearAlgebra.Bidiagonal, B::LinearAlgebra.LowerTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:99
- +(A::LinearAlgebra.Bidiagonal, B::LinearAlgebra.UnitLowerTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:99
- +(A::LinearAlgebra.Bidiagonal, B::LinearAlgebra.Diagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:120
- +(A::LinearAlgebra.Bidiagonal, B::LinearAlgebra.Tridiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:188
- +(A::LinearAlgebra.Bidiagonal, B::LinearAlgebra.SymTridiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:208
- +(A::LinearAlgebra.Bidiagonal{var"#s972", V} where {var"#s972"<:Number, V<:AbstractVector{var"#s972"}}, B::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:241
- +(A::LinearAlgebra.UnitLowerTriangular, B::LinearAlgebra.LowerTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/triangular.jl:647
- +(A::LinearAlgebra.UnitLowerTriangular, B::LinearAlgebra.UnitLowerTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/triangular.jl:649
- +(UL::LinearAlgebra.UnitLowerTriangular, J::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/uniformscaling.jl:181
- +(A::LinearAlgebra.UnitLowerTriangular, B::LinearAlgebra.Bidiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:91
- +(A::LinearAlgebra.UpperTriangular, B::LinearAlgebra.UpperTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/triangular.jl:642
- +(A::LinearAlgebra.UpperTriangular, B::LinearAlgebra.UnitUpperTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/triangular.jl:644
- +(x::LinearAlgebra.UpperTriangular, H::LinearAlgebra.UpperHessenberg) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/hessenberg.jl:112
- +(A::LinearAlgebra.UpperTriangular, B::LinearAlgebra.Bidiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:91
- +(A::SparseArrays.AbstractSparseMatrixCSC, B::SparseArrays.AbstractSparseMatrixCSC) in SparseArrays at /usr/share/julia/stdlib/v1.9/SparseArrays/src/sparsematrix.jl:2065
- +(x::SparseArrays.AbstractSparseVector, y::SparseArrays.AbstractSparseVector) in SparseArrays at /usr/share/julia/stdlib/v1.9/SparseArrays/src/sparsevector.jl:1516
- +(A::SparseArrays.AbstractSparseMatrixCSC, B::Array) in SparseArrays at /usr/share/julia/stdlib/v1.9/SparseArrays/src/sparsematrix.jl:2068
- +(A::SparseArrays.AbstractSparseMatrixCSC{Tv, Ti}, J::LinearAlgebra.UniformScaling{T}) where {T<:Number, Tv, Ti} in SparseArrays at /usr/share/julia/stdlib/v1.9/SparseArrays/src/sparsematrix.jl:4098
- +(r1::OrdinalRange, r2::OrdinalRange) in Base at range.jl:1441
- +(x::T, y::Integer) where T<:AbstractChar in Base at char.jl:237
- +(A::LinearAlgebra.SymTridiagonal, B::LinearAlgebra.SymTridiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/tridiag.jl:207
- +(A::LinearAlgebra.SymTridiagonal, B::LinearAlgebra.Symmetric) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/symmetric.jl:485
- +(A::LinearAlgebra.SymTridiagonal{var"#s970", V} where {var"#s970"<:Real, V<:AbstractVector{var"#s970"}}, B::LinearAlgebra.Hermitian) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/symmetric.jl:487
- +(x::LinearAlgebra.SymTridiagonal, H::LinearAlgebra.UpperHessenberg) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/hessenberg.jl:112
- +(A::LinearAlgebra.SymTridiagonal, B::LinearAlgebra.Diagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:150
- +(A::LinearAlgebra.SymTridiagonal, B::LinearAlgebra.Tridiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:164
- +(A::LinearAlgebra.SymTridiagonal, B::LinearAlgebra.Bidiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:218
- +(A::LinearAlgebra.SymTridiagonal{var"#s972", V} where {var"#s972"<:Number, V<:AbstractVector{var"#s972"}}, B::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:236
- +(y::AbstractFloat, x::Bool) in Base at bool.jl:176
- +(A::LinearAlgebra.Symmetric, B::LinearAlgebra.Symmetric) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/symmetric.jl:469
- +(A::LinearAlgebra.Symmetric, B::LinearAlgebra.SymTridiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/symmetric.jl:486
- +(S::LinearAlgebra.Symmetric, D::LinearAlgebra.Diagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/diagonal.jl:191
- +(A::LinearAlgebra.Symmetric{<:Any, <:SparseArrays.AbstractSparseMatrix}, B::SparseArrays.AbstractSparseMatrix) in SparseArrays at /usr/share/julia/stdlib/v1.9/SparseArrays/src/linalg.jl:15
- +(A::LinearAlgebra.Symmetric, B::SparseArrays.AbstractSparseMatrix) in SparseArrays at /usr/share/julia/stdlib/v1.9/SparseArrays/src/linalg.jl:18
- +(A::LinearAlgebra.Symmetric{<:Real, <:SparseArrays.AbstractSparseMatrix}, B::LinearAlgebra.Hermitian{<:Any, <:SparseArrays.AbstractSparseMatrix}) in SparseArrays at /usr/share/julia/stdlib/v1.9/SparseArrays/src/linalg.jl:25
- +(A::LinearAlgebra.Symmetric{var"#s971", S} where {var"#s971"<:Real, S<:(AbstractMatrix{<:var"#s971"})}, B::LinearAlgebra.Hermitian) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/symmetric.jl:484
- +(A::LinearAlgebra.Symmetric{<:Any, <:SparseArrays.AbstractSparseMatrix}, B::LinearAlgebra.Hermitian{<:Any, <:SparseArrays.AbstractSparseMatrix}) in SparseArrays at /usr/share/julia/stdlib/v1.9/SparseArrays/src/linalg.jl:23
- +(x::Dates.Instant) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/arithmetic.jl:4
- +(A::LinearAlgebra.Hermitian, B::LinearAlgebra.Hermitian) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/symmetric.jl:469
- +(A::LinearAlgebra.Hermitian, B::LinearAlgebra.SymTridiagonal{var"#s969", V} where {var"#s969"<:Real, V<:AbstractVector{var"#s969"}}) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/symmetric.jl:488
- +(H::LinearAlgebra.Hermitian, D::LinearAlgebra.Diagonal{var"#s972", V} where {var"#s972"<:Real, V<:AbstractVector{var"#s972"}}) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/diagonal.jl:197
- +(A::LinearAlgebra.Hermitian, J::LinearAlgebra.UniformScaling{<:Complex}) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/uniformscaling.jl:195
- +(A::LinearAlgebra.Hermitian{<:Any, <:SparseArrays.AbstractSparseMatrix}, B::SparseArrays.AbstractSparseMatrix) in SparseArrays at /usr/share/julia/stdlib/v1.9/SparseArrays/src/linalg.jl:15
- +(A::LinearAlgebra.Hermitian, B::SparseArrays.AbstractSparseMatrix) in SparseArrays at /usr/share/julia/stdlib/v1.9/SparseArrays/src/linalg.jl:18
- +(A::LinearAlgebra.Hermitian{<:Any, <:SparseArrays.AbstractSparseMatrix}, B::LinearAlgebra.Symmetric{<:Real, <:SparseArrays.AbstractSparseMatrix}) in SparseArrays at /usr/share/julia/stdlib/v1.9/SparseArrays/src/linalg.jl:26
- +(A::LinearAlgebra.Hermitian, B::LinearAlgebra.Symmetric{var"#s972", S} where {var"#s972"<:Real, S<:(AbstractMatrix{<:var"#s972"})}) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/symmetric.jl:483
- +(A::LinearAlgebra.Hermitian{<:Any, <:SparseArrays.AbstractSparseMatrix}, B::LinearAlgebra.Symmetric{<:Any, <:SparseArrays.AbstractSparseMatrix}) in SparseArrays at /usr/share/julia/stdlib/v1.9/SparseArrays/src/linalg.jl:24
- +(A::LinearAlgebra.UpperHessenberg, B::LinearAlgebra.UpperHessenberg) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/hessenberg.jl:104
- +(H::LinearAlgebra.UpperHessenberg, x::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/hessenberg.jl:111
- +(H::LinearAlgebra.UpperHessenberg, x::LinearAlgebra.Diagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/hessenberg.jl:111
- +(H::LinearAlgebra.UpperHessenberg, x::LinearAlgebra.Bidiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/hessenberg.jl:111
- +(H::LinearAlgebra.UpperHessenberg, x::LinearAlgebra.Tridiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/hessenberg.jl:111
- +(H::LinearAlgebra.UpperHessenberg, x::LinearAlgebra.SymTridiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/hessenberg.jl:111
- +(H::LinearAlgebra.UpperHessenberg, x::LinearAlgebra.UpperTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/hessenberg.jl:111
- +(H::LinearAlgebra.UpperHessenberg, x::LinearAlgebra.UnitUpperTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/hessenberg.jl:111
- +(z::Complex, w::Complex) in Base at complex.jl:291
- +(x::AbstractIrrational, y::AbstractIrrational) in Base at irrationals.jl:161
- +(index1::CartesianIndex{N}, index2::CartesianIndex{N}) where N in Base.IteratorsMD at multidimensional.jl:111
- +(A::BitArray, B::BitArray) in Base at bitarray.jl:1184
- +(x::Ptr, y::Integer) in Base at pointer.jl:167
- +(Da::LinearAlgebra.Diagonal, Db::LinearAlgebra.Diagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/diagonal.jl:184
- +(D::LinearAlgebra.Diagonal, S::LinearAlgebra.Symmetric) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/diagonal.jl:188
- +(D::LinearAlgebra.Diagonal{var"#s972", V} where {var"#s972"<:Real, V<:AbstractVector{var"#s972"}}, H::LinearAlgebra.Hermitian) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/diagonal.jl:194
- +(x::LinearAlgebra.Diagonal, H::LinearAlgebra.UpperHessenberg) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/hessenberg.jl:112
- +(A::LinearAlgebra.Diagonal, B::LinearAlgebra.Bidiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:130
- +(A::LinearAlgebra.Diagonal, B::LinearAlgebra.SymTridiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:140
- +(A::LinearAlgebra.Diagonal, B::LinearAlgebra.Tridiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:168
- +(A::LinearAlgebra.Diagonal{var"#s972", V} where {var"#s972"<:Number, V<:AbstractVector{var"#s972"}}, B::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:246
- +(r1::LinRange{T}, r2::LinRange{T}) where T in Base at range.jl:1448
- +(A::LinearAlgebra.LowerTriangular, B::LinearAlgebra.LowerTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/triangular.jl:643
- +(A::LinearAlgebra.LowerTriangular, B::LinearAlgebra.UnitLowerTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/triangular.jl:645
- +(A::LinearAlgebra.AbstractTriangular, B::LinearAlgebra.AbstractTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/triangular.jl:650
- +(A::LinearAlgebra.LowerTriangular, B::LinearAlgebra.Bidiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:91
- +(J::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/uniformscaling.jl:149
- +(J::LinearAlgebra.UniformScaling, x::Number) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/uniformscaling.jl:144
- +(J1::LinearAlgebra.UniformScaling, J2::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/uniformscaling.jl:150
- +(J::LinearAlgebra.UniformScaling, B::BitMatrix) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/uniformscaling.jl:152
- +(x::LinearAlgebra.UniformScaling, H::LinearAlgebra.UpperHessenberg) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/hessenberg.jl:112
- +(J::LinearAlgebra.UniformScaling, F::LinearAlgebra.Hessenberg) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/hessenberg.jl:648
- +(A::LinearAlgebra.UniformScaling, B::LinearAlgebra.Tridiagonal{var"#s972", V} where {var"#s972"<:Number, V<:AbstractVector{var"#s972"}}) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:250
- +(A::LinearAlgebra.UniformScaling, B::LinearAlgebra.SymTridiagonal{var"#s972", V} where {var"#s972"<:Number, V<:AbstractVector{var"#s972"}}) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:255
- +(A::LinearAlgebra.UniformScaling, B::LinearAlgebra.Bidiagonal{var"#s972", V} where {var"#s972"<:Number, V<:AbstractVector{var"#s972"}}) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:260
- +(A::LinearAlgebra.UniformScaling, B::LinearAlgebra.Diagonal{var"#s972", V} where {var"#s972"<:Number, V<:AbstractVector{var"#s972"}}) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/special.jl:265
- +(J::LinearAlgebra.UniformScaling{T}, A::SparseArrays.AbstractSparseMatrixCSC{Tv, Ti}) where {T<:Number, Tv, Ti} in SparseArrays at /usr/share/julia/stdlib/v1.9/SparseArrays/src/sparsematrix.jl:4100
- +(J::LinearAlgebra.UniformScaling, A::AbstractMatrix) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/uniformscaling.jl:153
- +(x::Base.TwicePrecision, y::Number) in Base at twiceprecision.jl:290
- +(x::Base.TwicePrecision{T}, y::Base.TwicePrecision{T}) where T in Base at twiceprecision.jl:296
- +(x::Base.TwicePrecision, y::Base.TwicePrecision) in Base at twiceprecision.jl:301
- +(y::Dates.TimeType, x::StridedArray{<:Union{Dates.CompoundPeriod, Dates.Period}}) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/deprecated.jl:18
- +(A::Array, Bs::Array...) in Base at arraymath.jl:12
- +(X::StridedArray{<:Union{Dates.CompoundPeriod, Dates.Period}}, Y::StridedArray{<:Union{Dates.CompoundPeriod, Dates.Period}}) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/deprecated.jl:62
- +(A::Array, B::SparseArrays.AbstractSparseMatrixCSC) in SparseArrays at /usr/share/julia/stdlib/v1.9/SparseArrays/src/sparsematrix.jl:2069
- +(x::StridedArray{<:Union{Dates.CompoundPeriod, Dates.Period}}) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/deprecated.jl:55
- +(x::StridedArray{<:Union{Dates.CompoundPeriod, Dates.Period}}, y::Dates.TimeType) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/deprecated.jl:10
- +(r1::StepRangeLen{T, R}, r2::StepRangeLen{T, R}) where {R<:Base.TwicePrecision, T} in Base at twiceprecision.jl:628
- +(r1::StepRangeLen{T, S}, r2::StepRangeLen{T, S}) where {T, S} in Base at range.jl:1464
- +(r1::Union{LinRange, OrdinalRange, StepRangeLen}, r2::Union{LinRange, OrdinalRange, StepRangeLen}) in Base at range.jl:1457
- +(A::AbstractArray, B::AbstractArray) in Base at arraymath.jl:6
- +(x::Rational, y::Integer) in Base at rational.jl:327
- +(a::Pkg.Resolve.VersionWeight, b::Pkg.Resolve.VersionWeight) in Pkg.Resolve at /usr/share/julia/stdlib/v1.9/Pkg/src/Resolve/versionweights.jl:22
- +(x::BigFloat, c::Union{UInt16, UInt32, UInt64, UInt8}) in Base.MPFR at mpfr.jl:402
- +(x::BigFloat, c::Union{Int16, Int32, Int64, Int8}) in Base.MPFR at mpfr.jl:410
- +(x::BigFloat, c::Union{Float16, Float32, Float64}) in Base.MPFR at mpfr.jl:418
- +(x::BigFloat, y::BigFloat) in Base.MPFR at mpfr.jl:395
- +(x::BigFloat, c::BigInt) in Base.MPFR at mpfr.jl:426
- +(a::BigFloat, b::BigFloat, c::BigFloat) in Base.MPFR at mpfr.jl:567
- +(a::BigFloat, b::BigFloat, c::BigFloat, d::BigFloat) in Base.MPFR at mpfr.jl:573
- +(a::BigFloat, b::BigFloat, c::BigFloat, d::BigFloat, e::BigFloat) in Base.MPFR at mpfr.jl:580
- +(z::Complex{Bool}, x::Bool) in Base at complex.jl:303
- +(z::Complex, x::Bool) in Base at complex.jl:310
- +(z::Complex{Bool}, x::Real) in Base at complex.jl:317
- +(z::Complex) in Base at complex.jl:289
- +(z::Complex, x::Real) in Base at complex.jl:329
- +(dt::Dates.Date, t::Dates.Time) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/arithmetic.jl:19
- +(dt::Dates.Date, y::Dates.Year) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/arithmetic.jl:27
- +(dt::Dates.Date, z::Dates.Month) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/arithmetic.jl:54
- +(x::Dates.Date, y::Dates.Quarter) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/arithmetic.jl:73
- +(x::Dates.Date, y::Dates.Week) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/arithmetic.jl:77
- +(x::Dates.Date, y::Dates.Day) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/arithmetic.jl:79
- +(x::Bool) in Base at bool.jl:163
- +(x::Integer, y::Ptr) in Base at pointer.jl:169
- +(y::Integer, x::Rational) in Base at rational.jl:334
- +(x::Integer, y::AbstractChar) in Base at char.jl:247
- +(x::Bool, y::T) where T<:AbstractFloat in Base at bool.jl:173
- +(x::Bool, y::Bool) in Base at bool.jl:166
- +(x::Bool, z::Complex{Bool}) in Base at complex.jl:302
- +(x::Real, z::Complex{Bool}) in Base at complex.jl:316
- +(x::Bool, z::Complex) in Base at complex.jl:309
- +(x::Real, z::Complex) in Base at complex.jl:328
- +(x::Dates.CompoundPeriod, y::Dates.Period) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/periods.jl:332
- +(x::Dates.CompoundPeriod, y::Dates.TimeType) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/periods.jl:362
- +(x::Dates.CompoundPeriod, y::Dates.CompoundPeriod) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/periods.jl:334
- +(B::BitMatrix, J::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/uniformscaling.jl:151
- +(A::AbstractMatrix, J::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/uniformscaling.jl:214
- +(x::AbstractArray{<:Number}) in Base at abstractarraymath.jl:220
- +(x::BigInt, c::Union{UInt16, UInt32, UInt64, UInt8}) in Base.GMP at gmp.jl:538
- +(x::BigInt, c::Union{Int16, Int32, Int64, Int8}) in Base.GMP at gmp.jl:544
- +(x::BigInt, y::BigInt) in Base.GMP at gmp.jl:490
- +(a::BigInt, b::BigInt, c::BigInt) in Base.GMP at gmp.jl:530
- +(a::BigInt, b::BigInt, c::BigInt, d::BigInt) in Base.GMP at gmp.jl:531
- +(a::BigInt, b::BigInt, c::BigInt, d::BigInt, e::BigInt) in Base.GMP at gmp.jl:532
- +(x::BigInt, y::BigInt, rest::BigInt...) in Base.GMP at gmp.jl:666
- +(a::Integer, b::Integer) in Base at int.jl:1038
- +(c::BigInt, x::BigFloat) in Base.MPFR at mpfr.jl:431
- +(x::Dates.Time, y::Dates.TimePeriod) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/arithmetic.jl:83
- +(t::Dates.Time, dt::Dates.Date) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/arithmetic.jl:20
- +(x::Rational{BigInt}, y::Rational{BigInt}) in Base.GMP.MPQ at gmp.jl:1027
- +(x::Rational) in Base at rational.jl:284
- +(x::Number, y::Base.TwicePrecision) in Base at twiceprecision.jl:294
- +(::Number, ::Missing) in Base at missing.jl:124
- +(x::Number, J::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/share/julia/stdlib/v1.9/LinearAlgebra/src/uniformscaling.jl:145
- +(x::Rational, y::Rational) in Base at rational.jl:298
- +(x::Number) in Base at operators.jl:515
- +(x::T, y::T) where T<:Number in Base at promotion.jl:485
- +(x::Number, y::Number) in Base at promotion.jl:410
- +(::Missing) in Base at missing.jl:101
- +(::Missing, ::Number) in Base at missing.jl:123
- +(x::Missing, y::Dates.AbstractTime) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/arithmetic.jl:89
- +(::Missing, ::Missing) in Base at missing.jl:122
- +(level::Base.CoreLogging.LogLevel, inc::Integer) in Base.CoreLogging at logging.jl:131
- +(a::Pkg.Resolve.FieldValue, b::Pkg.Resolve.FieldValue) in Pkg.Resolve at /usr/share/julia/stdlib/v1.9/Pkg/src/Resolve/fieldvalues.jl:43
- +(dt::Dates.DateTime, y::Dates.Year) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/arithmetic.jl:23
- +(dt::Dates.DateTime, z::Dates.Month) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/arithmetic.jl:47
- +(x::Dates.DateTime, y::Dates.Quarter) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/arithmetic.jl:75
- +(x::Dates.DateTime, y::Dates.Period) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/arithmetic.jl:81
- +(x::Dates.TimeType) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/arithmetic.jl:8
- +(x::Dates.AbstractTime, y::Missing) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/arithmetic.jl:88
- +(a::Dates.TimeType, b::Dates.Period, c::Dates.Period) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/periods.jl:353
- +(a::Dates.TimeType, b::Dates.Period, c::Dates.Period, d::Dates.Period...) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/periods.jl:354
- +(x::Dates.TimeType, y::Dates.CompoundPeriod) in Dates at /usr/share/julia/stdlib/v1.9/Dates/src/periods.jl:356
- +(a, b, c, xs...) in Base at operators.jl:578
Každá z těchto 207 možností představuje konkrétní implementaci sčítání pro argumenty uvedené v signatuře. Zvídavý uživatel může jedním prostým kliknutím rovnou nahlédnou zdrojový kód.
Pozor na ne/jednoznačnost!
Při volání metody se Julia snaží vždy vybrat tu "nejspecifičtější" vzhledem k typovému systému. Například:
f(x::Integer) = 2 * x
f(x::Number) = 3 * x
f (generic function with 4 methods)
f(1) # 1 "je" jak Integer tak Number a Integer <: Number
2
f(1.0) # 1.0 "je" jen Number
3.0
Integer <: Number
true
Samozřejmě hrozí nebezpečí nejednoznačnosti. Na takovou situaci nás naštěstí Julia upozorní.
f(x::Float64, y) = x + y
f(x, y::Float64) = x * y
f (generic function with 4 methods)
f(1.0, 2)
3.0
f(1, 2.0)
2.0
@which f(1.0, 2)
f(1.0, 2.0)
MethodError: f(::Float64, ::Float64) is ambiguous. Candidates: f(x::Float64, y) @ Main In[6]:1 f(x, y::Float64) @ Main In[6]:2 Possible fix, define f(::Float64, ::Float64) Stacktrace: [1] top-level scope @ In[13]:1
Funkce f
má aktuálně čtyři metody:
methods(f)
- f(x::Integer) in Main at In[8]:1
- f(x::Float64, y) in Main at In[6]:1
- f(x::Number) in Main at In[8]:2
- f(x, y::Float64) in Main at In[6]:2
1.2 Definice metod: parametrické metody
Podobně jako typy mohly mít parametry, mohou mít parametry i metody. Můžeme tak mít různé varianty metody v závislosti na jejich argumentech.
function same_type(x::T, y::T) where {T}
println(T)
return true
end
function same_type(x, y)
return false
end
same_type (generic function with 2 methods)
Díky anotaci se první metoda použije pouze v případě, že jsou argumenty stejného typu. Pokud nejsou, zavolá se druhá "obecná" metoda.
same_type(1, 2)
Int64
true
same_type(1.0, 2.0)
Float64
true
same_type(1, 2.0)
false
same_type(Int64, Int32)
DataType
true
methods(same_type)
- same_type(x::T, y::T) where T in Main at In[40]:1
- same_type(x, y) in Main at In[40]:6
Dále v anotaci i můžeme omezit typ T
samotný, nebo použít více typů:
function parametric_f(x::T, y::T, z::S) where { T <: Number, S <: AbstractString }
return nothing
end
parametric_f (generic function with 1 method)
Tato metoda vyžaduje, aby první dva argumenty byly stejného "číselného" typu T
a aby poslední argument byl podtypem typu AbstractString
.
1.3 Příklady a cvičení
V předchozí lekci jsme definovali vlastní typ pro racionální čísla. Pojďme nyní zadefinovat sčítání a další operace i pro ně.
"""
MyRational{T <: Integer} <: Number
_My_ home made rational number.
"""
struct MyRational{T <: Integer} <: Number
num::T
den::T
function MyRational(num::T, den::T) where { T <: Integer }
den == 0 && error("Zero denominator is forbidden by god!")
if den < 0
num *= -1
den *= -1
end
# divide by common factors
common = gcd(num, den)
new{T}(div(num, common), div(den, common))
end
end
MyRational
Použili jsme ještě jednu novinku a tou je docstring před definicí typu. Tímto způsobem ho můžete použít i před definicí metod, typů, maker. Později k němu můžete přistupovat pomocí integrované nápovědy, nebo ho použít při generování dokumentace (touto problematikou se budeme zabývat později během semestru). Všimněte si, že v docstringu můžeme používat Markdown.
?MyRational
search: MyRational
MyRational{T <: Integer} <: Number
My home made rational number.
Pokud chceme definovat novou metodu funkce, která je definována externě (v jiném modulu, o nich později), musíme ji explicitně importovat nebo musíme uvést celé její jméno. První možnost:
import Base.+
+(p::MyRational{T}, q::MyRational{T}) where { T <: Integer } =
MyRational(p.num * q.den + q.num * p.den, p.den * q.den)
+ (generic function with 208 methods)
Pojďme ji hned s nadšením otestovat.
p = MyRational(1, 2)
q = MyRational(2, 3)
p + q
7/6
Toto je sice správný výsledek, ale esteticky není uspokojivý.
Více oku lahodící vypisování našich racionálních čísel můžeme zajistit pomocí přidání metody k funkci show
(viz Custom Pretty-printing):
# bez explicitního `import Base.show`
Base.show(io::IO, q::MyRational{T}) where { T <: Integer } =
print(io, q.num, "/", q.den)
Potom dostaneme:
p + q
7/6
MyRational(2, -3)
-2/3
Pro úplnost můžeme jednoduše definovat násobení (binární operátor *
) a opačný prvek (unární operátor -
) a odčítání (binární operátor -
).
Všimněte si, jak v definici odčítání -- pěkně v souladu s matematickou definicí -- použijeme pouze sčítání a definici opačného prvku.
import Base.-, Base.*
*(p::MyRational{T}, q::MyRational{T}) where { T <: Integer } = MyRational(p.num * q.num, p.den * q.den)
-(p::MyRational{T}) where { T <: Integer} = MyRational(-one(T), one(T)) * p
# nebo: = MyRational(-p.num, p.den)
-(p::MyRational{T}, q::MyRational{T}) where { T <: Integer } = p + (-q)
- (generic function with 213 methods)
Otestujme správnou funkčnost algebraických operací mezi našimi racionálními čísly:
println("p = ", p, ", q = ", q)
p * q
p = 1/2, q = 2/3
1/3
-p
-1/2
p - q
-1/6
Cvičení: Inverze a mocnění v
Naše racionální čísla bychom ještě chtěli umocňovat, i na záporné exponenty (speciáně na , tedy invertovat). Dokážete vyřešit jak na to?
p ^ 3
1/8
@which p ^ 3
p ^ (-1)
MethodError: no method matching MyRational{Int64}(::Int64) Closest candidates are: (::Type{T})(::T) where T<:Number @ Core boot.jl:792 (::Type{T})(::AbstractChar) where T<:Union{AbstractChar, Number} @ Base char.jl:50 (::Type{T})(::Base.TwicePrecision) where T<:Number @ Base twiceprecision.jl:266 Stacktrace: [1] convert(#unused#::Type{MyRational{Int64}}, x::Int64) @ Base ./number.jl:7 [2] one(#unused#::Type{MyRational{Int64}}) @ Base ./number.jl:346 [3] one(x::MyRational{Int64}) @ Base ./number.jl:347 [4] inv(x::MyRational{Int64}) @ Base ./number.jl:255 [5] literal_pow(f::typeof(^), x::MyRational{Int64}, #unused#::Val{-1}) @ Base ./intfuncs.jl:335 [6] top-level scope @ In[24]:1
MyRational{T}(n::T) where { T <: Integer } = MyRational(n, one(T))
p ^ (-1)
/ not defined for MyRational{Int64} Stacktrace: [1] error(::String, ::String, ::Type) @ Base ./error.jl:44 [2] no_op_err(name::String, T::Type) @ Base ./promotion.jl:484 [3] /(x::MyRational{Int64}, y::MyRational{Int64}) @ Base ./promotion.jl:488 [4] inv(x::MyRational{Int64}) @ Base ./number.jl:255 [5] literal_pow(f::typeof(^), x::MyRational{Int64}, #unused#::Val{-1}) @ Base ./intfuncs.jl:335 [6] top-level scope @ In[28]:1
Base.:/(p::MyRational{T}, q::MyRational{T}) where { T <: Integer } = MyRational(p.num * q.den, p.den * q.num)
p ^ (-1)
2/1
Base.inv(q::MyRational{T}) where { T <: Integer } = MyRational(q.den, q.num)
inv(MyRational(3, 5))
5/3
-inv(MyRational(-3, 5))
5/3
p ^ (-2)
4/1
p ^ (-1)
2/1
inv(MyRational(0, 1))
Zero denominator is forbidden by god! Stacktrace: [1] error(s::String) @ Base ./error.jl:35 [2] MyRational(num::Int64, den::Int64) @ Main ./In[8]:12 [3] inv(q::MyRational{Int64}) @ Main ./In[35]:1 [4] top-level scope @ In[40]:1
Racionální čísla jsou v Julia k dispozici i bez našeho typu jako typ Rational{T}
. Jako konstruktor můžeme využít i dvojité lomítko:
2 // 3
2//3
4 // 2
2//1
Poznámka: Julia Rational
umí vytvořit exaktní reprezentaci strojového čísla (které z definice je vždy racionální číslo):
Rational(0.3)
5404319552844595//18014398509481984
Float64(Rational(0.3) - 3 // 10)
-1.1102230246251566e-17
Rational(0.5)
1//2
Cvičení: Komplexní čísla
Vytvořte vlastní typ MyComplex{T}
modelující komplexní čísla a vybavte ho standardními metodami sčítání +
, odčítání -
, násobení *
, dělení /
a inverze inv
.
struct MyComplex{T <: Real} <: Number
re::T
im::T
end
Base.show(io::IO, z::MyComplex{T}) where { T <: Real } =
if z.im >= 0
print(io, z.re, " + ", z.im, "ı")
else
print(io, z.re, " - ", -z.im, "ı")
end
z = MyComplex(1, 2)
z = MyComplex(-1, 2)
z = MyComplex(1, -2)
import Base.*, Base.+, Base.-, Base.inv, Base./
*(u::MyComplex{S}, v::MyComplex{T}) where {S, T} = nothing # FIX ME
+(u::MyComplex{S}, v::MyComplex{T}) where {S, T} = nothing
-(u::MyComplex{T}) where {T} = nothing
-(u::MyComplex{S}, v::MyComplex{T}) where {S, T} = nothing
function inv(u::MyComplex{T}) where {T}
# ...
return nothing
end
/(u::MyComplex{S}, v::MyComplex{T}) where {S, T} = nothing
w = MyComplex(3, 4)
z
z + w
z * w
inv(z) * MyComplex(1.0, -2.0)
MyComplex(1.0, -2.0) / MyComplex(1.0, -2.0)
z / z
Cvičení: Modulární multiplikativní grupa
Pro prvočíslo tvoří množina s operací násobení modulo grupu (tzv. modulární multiplikativní grupa; značí se , nebo ). O pak mluvíme jako o modulu.
Vytvořte v Julia typ, který bude parametrizovaný typem integeru a modulem , a bude modelovat výše uvedenou strukturu. Vhodně zadefinujte operaci *
, inv
a zajistěte pěkný výpis objektů tohoto typu.
Definujte metodu modulus
vracící modul.
Doplňte následující šablonu:
import Base.*, Base.inv, Base.show
using Primes
"""
Modular Multiplicative Group (MMG).
"""
struct MMG{T <: Integer, P} <: Number
value::T
function MMG(value::T, modulus::T) where { T <: Integer }
isprime(modulus) || error("modulus ($modulus) has to be prime!")
my_value = mod(value, modulus)
iszero(my_value) && error("0 is not acceptable!")
new{T, modulus}(my_value)
end
end
modulus(u::MMG{T, P}) where { T <: Integer, P } = P
function *(a::MMG{T, P}, b::MMG{T, P}) where { T <: Integer, P }
MMG(mod(a.value * b.value, P), P)
end
function inv(a::MMG{T, P}) where { T <: Integer, P }
_, u, _ = gcdx(a.value, P)
MMG(mod(u, P), P)
end
show(io::IO, a::MMG) = print(io, "|", a.value, "|_$(modulus(a))")
show (generic function with 288 methods)
Následuje několik ukázek použití.
a = MMG(3, 11); b = MMG(5, 11); c = MMG(5, 13)
|5|_13
typeof(a)
MMG{Int64, 11}
modulus(a)
11
a * b
|4|_11
# toto by šlo ještě vylepšit..., výchozí chování.
b * c
promotion of types MMG{Int64, 11} and MMG{Int64, 13} failed to change any arguments Stacktrace: [1] error(::String, ::String, ::String) @ Base ./error.jl:44 [2] sametype_error(input::Tuple{MMG{Int64, 11}, MMG{Int64, 13}}) @ Base ./promotion.jl:405 [3] not_sametype(x::Tuple{MMG{Int64, 11}, MMG{Int64, 13}}, y::Tuple{MMG{Int64, 11}, MMG{Int64, 13}}) @ Base ./promotion.jl:399 [4] promote @ ./promotion.jl:382 [inlined] [5] *(x::MMG{Int64, 11}, y::MMG{Int64, 13}) @ Base ./promotion.jl:411 [6] top-level scope @ In[58]:2
inv(a)
|4|_11
a * inv(a)
|1|_11
c * inv(c)
|1|_13
inv(c)
|8|_13
b ^ 123
|4|_11
MMG(0, 3)
0 is not acceptable! Stacktrace: [1] error(s::String) @ Base ./error.jl:35 [2] MMG(value::Int64, modulus::Int64) @ Main ./In[53]:14 [3] top-level scope @ In[64]:1
1.4 Definice metod: poziční argumenty
Už víme, jak v definici metody anotovat typy argumentů a návratové hodnoty. Pro připomenutí:
F(x::Int64)::Float64 = x / (abs(x) + 1)
F (generic function with 1 method)
F(1)
0.5
F(0)
0.0
Ovšem:
F("0")
MethodError: no method matching F(::String) Closest candidates are: F(::Int64) @ Main In[1]:1 Stacktrace: [1] top-level scope @ In[4]:1
Asi je jasné, jak definovat metodu s více pozičními argumenty.
V této části lekce si ukážeme, jak argumentům přiřazovat výchozí hodnoty, jak definovat metody s proměnlivým počtem argumentů, či jak definovat metody s argumenty ve tvaru keyword=value
, u nichž nezávisí na pořadí.
U všech níže uvedených způsobů předání argumentů lze uvést i typovou anotaci.
Nepovinné argumenty / výchozí hodnoty
V definici funkce lze pomocí operátoru =
přiřadit "posledním" (od zadu, jinak by zápis nebyl jednoznačně interpretovatelný) argumentům výchozí hodnoty, které poté při volání není potřeba vypisovat.
Například:
h(x, y=2) = x + y
h (generic function with 2 methods)
h(1)
3
h(1, 1)
2
I v tomto zápisu lze případně anotovat typ proměnné y
pomocí ::
.
...
, "varargs"
Operátor Metody mohou mít přirozeně více argumentů. Následující funkce má právě tři poziční argumenty:
g(x, y, z) = x + y + z
g (generic function with 1 method)
Argumenty jí můžeme předat buď explicitně jeden po jednom, nebo je předat v tuple a použít ...
operátor.
g(1, 2, 3)
6
t = (1, 2, 3)
g(t...)
6
Ale pozor, následující volání selže. g
nemá definovánu metodu, která by si poradila s tuplem na vstupu.
g(t)
MethodError: no method matching g(::Tuple{Int64, Int64, Int64}) Closest candidates are: g(::Any, ::Any, ::Any) @ Main In[8]:1 Stacktrace: [1] top-level scope @ In[11]:1
Operátor ...
můžeme použít i v definici funkce samotné.
Umožňuje nám pak vytvořit funkci mající měnící se počet argumentů (variable number of arguments, "varargs").
Následující funkce má jeden nebo více pozičních argumentů:
H(x, args...) = println(args)
H (generic function with 1 method)
H(1)
()
()
()
(,) # <- blbost
syntax: unexpected "," Stacktrace: [1] top-level scope @ In[15]:1
(1)
1
(1,)
(1,)
H(1, 2)
(2,)
typeof((2))
Int64
typeof((2,))
Tuple{Int64}
H(1, 2, 3)
(2, 3)
Zamyslete se nad následujícími třemi ukázkami.
H(1, ("a", "b", "c"))
(("a", "b", "c"),)
H(1, ("a", "b", "c")...)
("a", "b", "c")
H(1, "a", "b", "c")
("a", "b", "c")
Typický reprezentant:
println(1, 2, 3, "Ahoj!")
123Ahoj!
keyword=value
(keyword arguments, "kwargs")
Argumenty tvaru Jakmile počet argumentů přeroste jistou hranici (pro mě někde kolem 3), tak může být vhodné místo pozičního předávání argumentů použít zadávání pomocí klíče a hodnoty u kterých poté nezávisí na pořadí.
Těmto argumentům také lze přiřazovat výchozí hodnoty.
Takovéto argumenty od těch pozičních oddělíme středníkem ;
v signatuře funkce:
G(x, y; operator=+) = operator(x, y)
G (generic function with 1 method)
G(1, 2)
3
G(2, 3, operator=*)
6
G(2, 3, *)
MethodError: no method matching G(::Int64, ::Int64, ::typeof(*)) Closest candidates are: G(::Any, ::Any; operator) @ Main In[26]:1 Stacktrace: [1] top-level scope @ In[29]:1
Klíči nemusí být přiřazena výchozí hodnota, ale tuto možnost asi často nepoužijeme.
Julia podporuje i neomezený počet těchto argumentů. Uvažme lehce esoterickou funkci s následující signaturou:
J(args...; kwargs...) = println(kwargs)
J (generic function with 1 method)
J(1, 2, a=1, b="x")
Base.Pairs{Symbol, Any, Tuple{Symbol, Symbol}, NamedTuple{(:a, :b), Tuple{Int64, String}}}(:a => 1, :b => "x")
V proměnné kwargs
je poté "slovník", kde pod symbolem odpovídajícím klíči uložena předávána hodnota.
function kwargs_example(; kwargs...)
println(kwargs[:a])
end
kwargs_example (generic function with 1 method)
kwargs_example(a=1)
1
kwargs_example(b=10)
type NamedTuple has no field a Stacktrace: [1] getindex @ ./namedtuple.jl:137 [inlined] [2] getindex(v::Base.Pairs{Symbol, Int64, Tuple{Symbol}, NamedTuple{(:b,), Tuple{Int64}}}, key::Symbol) @ Base.Iterators ./iterators.jl:320 [3] kwargs_example(; kwargs::Base.Pairs{Symbol, Int64, Tuple{Symbol}, NamedTuple{(:b,), Tuple{Int64}}}) @ Main ./In[32]:2 [4] top-level scope @ In[34]:1
function ♋(x)
return x + 1
end
♋ (generic function with 1 method)
♋(x=1)
MethodError: no method matching ♋(; x::Int64) Closest candidates are: ♋(::Any) got unsupported keyword argument "x" @ Main In[35]:1 Stacktrace: [1] top-level scope @ In[36]:1
1.5 Návratová hodnota
V dosavadních příkladech metod jsme vždy vraceli hodnotu naposledy vyhodnoceného výrazu, často byl dokonce jenom jeden.
V mnoha situacích ale chceme vrátit hodnotu i z jiného místa, než konce těla.
K tomu nepřekvapivě slouží klíčové slovo return
.
function func(x::Integer)
# some funny stuff
for j = 1:10
j >= x && return j
end
return 42
end
func(-10)
func(3)
func(15)
Pomocí tuplů můžeme vracet i "více" hodnot.
function func_tuple(x)
return (x, x^2)
end
Pak je vhodné výsledek rovnou přiřadit do dvou proměnných:
a, b = func_tuple(10)
a
b
Nebo můžeme samozřejmě přijmout celý tuple.
c = func_tuple(10)
c
2. Makra a metaprogramování
Pod metaprogramováním máme na mysli schopnost programu generovat, či modifikovat, svůj zdrojový kód. Makra v Julia jsou inspirována Lispem. Nejde jen o pouhé textové transformace jako v případě maker v C/C++. V Julia má programátor přímý přístup k vnitřní reprezentaci zdrojového kódu.
Ukažme si, jak Julia zpracovává zdrojový kód. Na počátku máme řetězec. Například:
source = "1 + 2"
"1 + 2"
Co s tímto zdrojovým kódem udělá Julia parser?
Vytvoří objekt typu Expr
:
ex = Meta.parse(source)
:(1 + 2)
typeof(ex)
Expr
Objekt typu Expr
obsahuje v zásadě dvě informace:
ex.head # symbol udávající význam
:call
ex.args # argumenty
3-element Vector{Any}: :+ 1 2
Přehledně lze tyto informace vypsat pomocí metody dump
:
dump(ex)
Expr head: Symbol call args: Array{Any}((3,)) 1: Symbol + 2: Int64 1 3: Int64 2
Interpretace tohoto příkladu je nasnadě.
Reprezentuje volání (call) metody +
s Int64
argumenty 1
a 2
.
Tyto výrazy lze přirozeně vytvářet i přímo pomocí konstruktoru Expr
.
Za chvilku si ale ukážeme další způsoby, jak výrazy vytvářet, tento by nebyl příliš efektivní.
myex = Expr(:call, :+, 1, 2)
:(1 + 2)
ex == myex
true
Výrazy dohromady vytváří stromovou strukturu (AST -- abstract syntax tree), lze je do sebe zanořovat:
dump(Meta.parse("(1 + 2) / 3"))
Expr head: Symbol call args: Array{Any}((3,)) 1: Symbol / 2: Expr head: Symbol call args: Array{Any}((3,)) 1: Symbol + 2: Int64 1 3: Int64 2 3: Int64 3
A konečně, výrazy můžeme finálně vyhodnotit pomocí metody eval
.
eval(ex)
3
eval(myex)
3
Dalším způsobem vytváření objektů typu Expr
je quoting (kód v uvozovkách :-)).
Toho lze docílit dvěma způsoby.
Pro menší výrazy se hodí zápis pomocí :
, za kterou v závorce uvedeme Julia výraz:
myex2 = :((1 + 2) / 3)
:((1 + 2) / 3)
dump(:(f(1+1)))
Expr head: Symbol call args: Array{Any}((2,)) 1: Symbol f 2: Expr head: Symbol call args: Array{Any}((3,)) 1: Symbol + 2: Int64 1 3: Int64 1
Proměnné se ve výrazu uloží pod symboly, ne pod svými hodnotami!
x = 1
dump(:(x + 1))
Expr head: Symbol call args: Array{Any}((3,)) 1: Symbol + 2: Symbol x 3: Int64 1
Pokud bychom chtěli použít skutečně hodnotu proměnné, pak k tomu můžeme použít interpolační symbol $
:
x = 1
dump(:($x + 1))
Expr head: Symbol call args: Array{Any}((3,)) 1: Symbol + 2: Int64 1 3: Int64 1
Pro větší výrazy můžeme použít quote blok.
ex = quote
for j = 1:10
println(j)
end
end
quote #= In[71]:2 =# for j = 1:10 #= In[71]:3 =# println(j) #= In[71]:4 =# end end
typeof(ex)
Expr
dump(ex)
Expr head: Symbol block args: Array{Any}((2,)) 1: LineNumberNode line: Int64 2 file: Symbol In[71] 2: Expr head: Symbol for args: Array{Any}((2,)) 1: Expr head: Symbol = args: Array{Any}((2,)) 1: Symbol j 2: Expr head: Symbol call args: Array{Any}((3,)) 1: Symbol : 2: Int64 1 3: Int64 10 2: Expr head: Symbol block args: Array{Any}((3,)) 1: LineNumberNode line: Int64 3 file: Symbol In[71] 2: Expr head: Symbol call args: Array{Any}((2,)) 1: Symbol println 2: Symbol j 3: LineNumberNode line: Int64 4 file: Symbol In[71]
2.1 Makra
Makra akceptují jako argumenty výrazy (Expr
), literály nebo symboly a vrací výraz (Expr
).
Makra definujeme pomocí klíčového slova macro
.
Ve zdrojovém kódu poté makra používáme s prefixem @
.
Makra se aplikují při parsování zdrojového kódu, před jeho odesláním kompilátoru.
Ukažme si nejprve, jak je v AST reprezentováno voláni funkce.
f(a, b) = a + b
dump(Meta.parse("f(x, y)"))
Expr head: Symbol call args: Array{Any}((3,)) 1: Symbol f 2: Symbol x 3: Symbol y
Následující makro vypíše argumenty volání funkce.
macro show_args(expr::Expr)
println("Arguments: ", expr.args[2:end])
end
@show_args (macro with 1 method)
Například:
@show_args f(1, 2)
Arguments: Any[1, 2]
x = 42; y = 11
@show_args f(x, y)
Arguments: Any[:x, :y]
dump(:(f(x, y)))
Expr head: Symbol call args: Array{Any}((3,)) 1: Symbol f 2: Symbol x 3: Symbol y
dump(:(f(1, 2)))
Expr head: Symbol call args: Array{Any}((3,)) 1: Symbol f 2: Int64 1 3: Int64 2
To není přesně to co bychom asi chtěli (i když...). Pokud chceme vypsat i hodnoty proměnných, musíme trochu zapracovat....
macro show_args(expr::Expr)
println("Arguments: ")
for arg in expr.args[2:end]
if typeof(arg) == Symbol
println(arg, " = ", eval(:($arg)))
else
println(arg)
end
end
end
@show_args (macro with 1 method)
@show_args f(1, 2)
Arguments: 1 2
@show_args f(x, y)
Arguments: x = 42 y = 11
@show_args f(1, x)
Arguments: 1 x = 42
Občas pomocí maker chceme modifikovat prostředí, v kterém se volají. K tomu můžeme použít metodu esc
.
V prvním případě je x
pouze lokální proměnná a nemá vztah ke "globální" proměnné x
.
macro zerox()
return :(x = 0)
end
macro zerox2()
return esc(:(x = 0))
end
@zerox2 (macro with 1 method)
x = 42
42
@zerox
0
x
42
@zerox2
0
x
0
Psaní maker nemusí být úplně jednoduché. Více o této problematice se můžet dozvědět v dokumentaci.
Ve zbytku této části si ukážeme některá užitečná makra a zkusíme pár vlastních maker vytvořit. Výčet není zdaleka vyčerpávající a jde spíše o ochutnávku. Některým z těchto partií se ještě budeme věnovat později během semestru.
@which
2.2 Jak je již bylo zmíněno, pod jedním symbolem "funkce" se může skrývat mnoho a mnoho metod.
Občas nemusí být úplně jasné, která z nich se vlastně volá.
Makro @which
nám umožňuje dohledat o kterou metodu se v konkrétním případě jedná.
@which 2^3
@which 2.0^3.0
g1(x::Int64) = x + 1
g1(x::Float64) = x - 1
g1 (generic function with 2 methods)
@which g1(1.0)
@which g1(1)
@debug
, @info
, @warn
, @error
2.3 Tato makra slouží k informování uživatele, lze i kontrolvat na jaké úrovni se logování provádí (k tomu slouží modul Logging
, kterému se budeme věnovat při probírání standardní knihovny).
@debug "???"
@info "Hi!"
[ Info: Hi!
@warn "Beware!"
┌ Warning: Beware! └ @ Main In[107]:1
@error "Unable to compute!"
┌ Error: Unable to compute! └ @ Main In[108]:1
@time
, @timed
a @timev
2.4 Pomocí těchto maker můžeme měřit dobu běhu programu.
Jde o jednodušší variantu makra @benchmark
z balíčku BenchmarkTools.jl
.
Tato tři makra se liší pouze způsobem výstupu.
a = rand(1_000);
a[1:10]
10-element Vector{Float64}: 0.9859863295332932 0.7943897661309485 0.07491394795078443 0.5586316879179708 0.2507049281773901 0.5398690734367008 0.2801221089055207 0.5818573410558918 0.3597397644321122 0.9068556881081796
@time sort(a)
0.000046 seconds (3 allocations: 18.062 KiB)
1000-element Vector{Float64}: 0.0013617849273158678 0.001492430610654738 0.0027916357770800815 0.004425145061687896 0.00462979799190355 0.005003422002353375 0.005913901900464591 0.00599342810274639 0.006254358645049418 0.006694562716498265 0.00676392413181548 0.0068533882179516725 0.0070842856865499515 ⋮ 0.9885930279114615 0.9908113145251702 0.990835089436812 0.9924497970212934 0.9931087899481669 0.9937118644399341 0.9943065019666945 0.9962440813220133 0.9973942641891401 0.998216952917519 0.9989806183626024 0.9999144279858845
d
jako dictionary:
@timed sort(a)
(value = [0.0013617849273158678, 0.001492430610654738, 0.0027916357770800815, 0.004425145061687896, 0.00462979799190355, 0.005003422002353375, 0.005913901900464591, 0.00599342810274639, 0.006254358645049418, 0.006694562716498265 … 0.990835089436812, 0.9924497970212934, 0.9931087899481669, 0.9937118644399341, 0.9943065019666945, 0.9962440813220133, 0.9973942641891401, 0.998216952917519, 0.9989806183626024, 0.9999144279858845], time = 2.6949e-5, bytes = 18496, gctime = 0.0, gcstats = Base.GC_Diff(18496, 0, 0, 0, 3, 0, 0, 0, 0))
v
jako verbose, čili podrobnější:
@timev sort(a)
0.000032 seconds (3 allocations: 18.062 KiB) elapsed time (ns): 32352 gc time (ns): 0 bytes allocated: 18496 pool allocs: 0 non-pool GC allocs: 3 minor collections: 0 full collections: 0
1000-element Vector{Float64}: 0.0013617849273158678 0.001492430610654738 0.0027916357770800815 0.004425145061687896 0.00462979799190355 0.005003422002353375 0.005913901900464591 0.00599342810274639 0.006254358645049418 0.006694562716498265 0.00676392413181548 0.0068533882179516725 0.0070842856865499515 ⋮ 0.9885930279114615 0.9908113145251702 0.990835089436812 0.9924497970212934 0.9931087899481669 0.9937118644399341 0.9943065019666945 0.9962440813220133 0.9973942641891401 0.998216952917519 0.9989806183626024 0.9999144279858845
@inbounds
a @simd
2.5 Makro @inbound
"vypne" kontrolování používání správných indexů polí.
Makro @simd
umožňuje kompilátoru větši možnosti optimalizace for
cyklu, viz dokumentaci.
a = [1,2,3,4]
4-element Vector{Int64}: 1 2 3 4
a[2]
2
a[5]
BoundsError: attempt to access 4-element Vector{Int64} at index [5] Stacktrace: [1] getindex(A::Vector{Int64}, i1::Int64) @ Base ./essentials.jl:13 [2] top-level scope @ In[120]:1
using BenchmarkTools
function f1(a::Vector{Float64}, n)
val = zero(eltype(a))
for j = 1:n
val += a[j]
end
return val
end
function f2(a::Vector{Float64}, n)
val = zero(eltype(a))
@simd for j = 1:n
@inbounds val += a[j]
end
return val
end
function f3(a::Vector{Float64}, n)
val = zero(eltype(a))
@inbounds for j = 1:n
val += a[j]
end
return val
end
f3 (generic function with 1 method)
a = rand(10^8);
@benchmark f1($a, 10^8)
BenchmarkTools.Trial: 37 samples with 1 evaluation. Range (min … max): 133.063 ms … 140.297 ms ┊ GC (min … max): 0.00% … 0.00% Time (median): 135.544 ms ┊ GC (median): 0.00% Time (mean ± σ): 135.560 ms ± 1.778 ms ┊ GC (mean ± σ): 0.00% ± 0.00% ▄ █ ▄▁ ▁▁ ▁▁ ▆▆▆▁▆▆▁▆█▁█▁▁██▁▁▁▁▁██▆▁▆▆▁▆▆▁▆▆██▁▁▁▁▁▁▁▁▁▁▁▁▆▁▁▁▆▁▁▆▁▁▁▁▁▁▆ ▁ 133 ms Histogram: frequency by time 140 ms < Memory estimate: 0 bytes, allocs estimate: 0.
@benchmark f2($a, 10^8)
BenchmarkTools.Trial: 101 samples with 1 evaluation. Range (min … max): 45.060 ms … 58.767 ms ┊ GC (min … max): 0.00% … 0.00% Time (median): 49.662 ms ┊ GC (median): 0.00% Time (mean ± σ): 49.614 ms ± 2.327 ms ┊ GC (mean ± σ): 0.00% ± 0.00% ▂ ▄ ▄ ▆ ▆█ ▂ ▂ ▆▆▄▄▄▁▄█▁▄█▁▁▄▄▁▄▁▄▆█▄▄█▆███▆█▄███▆▆▁▆▆▁█▄█▁▄▁▄█▄▁▄▄▆▁▄▁▄▁▄ ▄ 45.1 ms Histogram: frequency by time 54.3 ms < Memory estimate: 0 bytes, allocs estimate: 0.
@benchmark f1($a, 10^6)
BenchmarkTools.Trial: 3435 samples with 1 evaluation. Range (min … max): 1.223 ms … 2.820 ms ┊ GC (min … max): 0.00% … 0.00% Time (median): 1.365 ms ┊ GC (median): 0.00% Time (mean ± σ): 1.445 ms ± 232.085 μs ┊ GC (mean ± σ): 0.00% ± 0.00% █ █▇▅▅▅▅▄▄▃▃▃▃▃▃▃▃▃▃▃▂▂▂▂▂▂▂▂▂▂▂▂▁▂▂▁▂▁▂▁▁▁▁▁▁▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▂ 1.22 ms Histogram: frequency by time 2.19 ms < Memory estimate: 0 bytes, allocs estimate: 0.
@benchmark f2($a, 10^6)
BenchmarkTools.Trial: 10000 samples with 1 evaluation. Range (min … max): 246.318 μs … 1.539 ms ┊ GC (min … max): 0.00% … 0.00% Time (median): 373.841 μs ┊ GC (median): 0.00% Time (mean ± σ): 455.668 μs ± 202.105 μs ┊ GC (mean ± σ): 0.00% ± 0.00% ▂▆█▅▂▁ ▂▅███████▇▇▆▅▅▄▄▃▃▃▃▃▃▂▂▃▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▁▂▁▁▂▁▁▁▁▁ ▃ 246 μs Histogram: frequency by time 1.12 ms < Memory estimate: 0 bytes, allocs estimate: 0.
@benchmark f3($a, 10^6)
BenchmarkTools.Trial: 3546 samples with 1 evaluation. Range (min … max): 1.223 ms … 2.337 ms ┊ GC (min … max): 0.00% … 0.00% Time (median): 1.331 ms ┊ GC (median): 0.00% Time (mean ± σ): 1.401 ms ± 182.668 μs ┊ GC (mean ± σ): 0.00% ± 0.00% █▆ ███▆▆▇█▆▅▄▅▄▄▃▃▄▃▃▃▃▃▃▃▃▃▃▂▂▃▂▃▂▂▂▂▂▂▂▂▂▂▂▂▂▂▁▂▁▂▁▁▂▁▁▁▁▁▁▁ ▂ 1.22 ms Histogram: frequency by time 1.97 ms < Memory estimate: 0 bytes, allocs estimate: 0.
@test
a @testset
2.6 Tato makra z modulu Test
nám umožňují přehledně testovat náš kód. Pomocí @testset
můžeme sdružit více testů dohromady a pojmenovat je (bere řetězec a blok). Druhé makro testuje, jestli výraz je pravdivý nebo nepravdivý. Například:
using Test
@testset "isodd method" begin
@test isodd(3) == true
@test isodd(2) == false
end
Dále máme k dispozici @test_throws
pro testování vyvolání výjimky.
@code_native
2.6 a.k.a "We need to go deeper..."
function g(x::Int64)
return x + 1
end
g (generic function with 2 methods)
@code_native g(2)
.text .file "g" .globl julia_g_4153 # -- Begin function julia_g_4153 .p2align 4, 0x90 .type julia_g_4153,@function julia_g_4153: # @julia_g_4153 ; ┌ @ In[129]:1 within `g` .cfi_startproc # %bb.0: # %top pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset %rbp, -16 movq %rsp, %rbp .cfi_def_cfa_register %rbp ; │ @ In[129]:2 within `g` ; │┌ @ int.jl:87 within `+` leaq 1(%rdi), %rax ; │└ popq %rbp .cfi_def_cfa %rsp, 8 retq .Lfunc_end0: .size julia_g_4153, .Lfunc_end0-julia_g_4153 .cfi_endproc ; └ # -- End function .section ".note.GNU-stack","",@progbits
ProgressMeter.jl
2.7using ProgressMeter
@showprogress for i in 1:50
sleep(0.1)
end
Progress: 100%|█████████████████████████████████████████| Time: 0:00:06
@showprogress dt=1 desc="Computing..." for i in 1:50
sleep(0.1)
end
Computing... 100%|███████████████████████████████████████| Time: 0:00:05
Cvičení
Vytvořte makro @dotimes
, které zadaný výraz provede několikrát za sebou.
Přesněji @dotimes n body
provede body
přesně n
krát.
macro dotimes(n, body)
quote
for i = 1:$n
$body
end
end
end
@dotimes 2 println("Hi!")
body = 1
n = 3
@dotimes 2 println("Hi!")
body
n
Cvičení: generování kódu
Definujme vlastní "číselný" typ:
struct MyNumber <: Number
value::Float64
end
Vygenerujte kód, který zadefinuje funkce sin
, cos
, log
, exp
pro tento typ.
for func in [:sin, :cos, :log, :exp]
eval(:(Base.$func(x::MyNumber) = MyNumber($func(x.value))))
end
sin(MyNumber(0.3))
cos(MyNumber(0.3))
exp(MyNumber(0.3))
log(MyNumber(0.3))
for
cyklu
Cvičení: Sledování průběhu Vyvořte makro, které bude zobrazovat jednoduchý průběh vyhodnocování for
cyklu. Tj.
@progress for j = 1:n
# ...
end
bude efektivně
for j = 1:n
# ...
println(j)
end
Případně se můžete pokusit výpis i více zkrášlit.
Zde jde samozřejmě o cvičení práce s makry.
Julia jinak má poměrně excelentní balíček ProgressMeter.jl
poskytující přesně tuto funkcionalitu.
macro progress(expr)
# ....
return expr
end
@progress for j=1:5
sleep(1)
end
Řešení některých příkladů
Inverze.
import Base.inv
inv(p::MyRational{T}) where { T <: Integer } = MyRational(p.den, p.num)
inv(p)
p^(-3)
Modulární multiplikativní grupa.
import Base.*, Base.inv, Base.show
using Primes
struct MMG{T <: Integer, P} <: Number
value::T
function MMG(value::T, modulus::T) where { T <: Integer }
isprime(modulus) || error("Modulus has to be prime!")
new{T, modulus}(mod(value, modulus))
end
end
modulus(u::MMG{T, P}) where { T <: Integer, P } = P
function *(a::MMG{T, P}, b::MMG{T, P}) where { T <: Integer, P }
return MMG(mod(a.value * b.value, P), P)
end
function inv(a::MMG{T, P}) where { T <: Integer, P }
# d = u * a + v * P
d, u, v = gcdx(a.value, P)
return MMG(mod(u, P), P)
end
show(io::IO, u::MMG) = print(io, u.value)
Makro dotimes
.
macro dotimes(n, body)
quote
for i = 1:$n
$body
end
end
end
Průběh for
cyklu.
macro progress(expr)
push!(expr.args[2].args, :(println(j)))
return expr
end