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 2025/2026 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()Julia Version 1.12.0 Commit b907bd0600f (2025-10-07 15:42 UTC) Build Info: Official https://julialang.org release Platform Info: OS: Linux (x86_64-linux-gnu) CPU: 8 × Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz WORD_SIZE: 64 LLVM: libLLVM-18.1.7 (ORCJIT, skylake) GC: Built with stock GC Threads: 1 default, 1 interactive, 1 GC (on 8 virtual cores)
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::BigInt, y::BigInt) in Base.GMP at gmp.jl:502
- +(a::BigInt, b::BigInt, c::BigInt) in Base.GMP at gmp.jl:542
- +(a::BigInt, b::BigInt, c::BigInt, d::BigInt) in Base.GMP at gmp.jl:543
- +(a::BigInt, b::BigInt, c::BigInt, d::BigInt, e::BigInt) in Base.GMP at gmp.jl:544
- +(x::BigInt, y::BigInt, rest::BigInt...) in Base.GMP at gmp.jl:679
- +(c::BigInt, x::BigFloat) in Base.MPFR at mpfr.jl:607
- +(x::BigInt, c::Union{UInt16, UInt32, UInt64, UInt8}) in Base.GMP at gmp.jl:550
- +(x::BigInt, c::Union{Int16, Int32, Int64, Int8}) in Base.GMP at gmp.jl:556
- +(x::Bool, z::Complex{Bool}) in Base at complex.jl:308
- +(x::Bool, y::Bool) in Base at bool.jl:168
- +(x::Bool) in Base at bool.jl:165
- +(x::Bool, z::Complex) in Base at complex.jl:315
- +(x::Bool, y::T) where T<:AbstractFloat in Base at bool.jl:175
- +(::Missing, ::Missing) in Base at missing.jl:122
- +(::Missing) in Base at missing.jl:101
- +(x::Missing, y::Dates.AbstractTime) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/arithmetic.jl:91
- +(::Missing, ::Number) in Base at missing.jl:123
- +(z::Complex{Bool}, x::Bool) in Base at complex.jl:309
- +(z::Complex{Bool}, x::Real) in Base at complex.jl:323
- +(level::Base.CoreLogging.LogLevel, inc::Integer) in Base.CoreLogging at logging/logging.jl:132
- +(B::BitMatrix, J::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/uniformscaling.jl:154
- +(x::BigFloat, c::BigInt) in Base.MPFR at mpfr.jl:602
- +(a::BigFloat, b::BigFloat, c::BigFloat, d::BigFloat, e::BigFloat) in Base.MPFR at mpfr.jl:758
- +(x::BigFloat, y::BigFloat) in Base.MPFR at mpfr.jl:571
- +(a::BigFloat, b::BigFloat, c::BigFloat) in Base.MPFR at mpfr.jl:745
- +(a::BigFloat, b::BigFloat, c::BigFloat, d::BigFloat) in Base.MPFR at mpfr.jl:751
- +(x::BigFloat, c::Union{UInt16, UInt32, UInt64, UInt8}) in Base.MPFR at mpfr.jl:578
- +(x::BigFloat, c::Union{Int16, Int32, Int64, Int8}) in Base.MPFR at mpfr.jl:586
- +(x::BigFloat, c::Union{Float16, Float32, Float64}) in Base.MPFR at mpfr.jl:594
- +(t::Dates.Time, dt::Dates.Date) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/arithmetic.jl:22
- +(x::Dates.Time, y::Dates.TimePeriod) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/arithmetic.jl:85
- +(x::Rational{BigInt}, y::Rational{BigInt}) in Base.GMP.MPQ at gmp.jl:1062
- +(x::Dates.CompoundPeriod, y::Dates.CompoundPeriod) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/periods.jl:335
- +(x::Dates.CompoundPeriod, y::Dates.Period) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/periods.jl:333
- +(x::Dates.CompoundPeriod, y::Dates.TimeType) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/periods.jl:363
- +(dt::Dates.Date, t::Dates.Time) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/arithmetic.jl:21
- +(dt::Dates.Date, y::Dates.Year) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/arithmetic.jl:29
- +(dt::Dates.Date, z::Dates.Month) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/arithmetic.jl:56
- +(x::Dates.Date, y::Dates.Quarter) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/arithmetic.jl:75
- +(x::Dates.Date, y::Dates.Week) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/arithmetic.jl:79
- +(x::Dates.Date, y::Dates.Day) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/arithmetic.jl:81
- +(dt::Dates.DateTime, y::Dates.Year) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/arithmetic.jl:25
- +(dt::Dates.DateTime, z::Dates.Month) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/arithmetic.jl:49
- +(x::Dates.DateTime, y::Dates.Quarter) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/arithmetic.jl:77
- +(x::Dates.DateTime, y::Dates.Period) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/arithmetic.jl:83
- +(A::Array, Bs::Array...) in Base at arraymath.jl:12
- +(z::Complex, x::Bool) in Base at complex.jl:316
- +(z::Complex, x::Real) in Base at complex.jl:335
- +(z::Complex, w::Complex) in Base at complex.jl:297
- +(z::Complex) in Base at complex.jl:295
- +(r1::OrdinalRange, r2::OrdinalRange) in Base at range.jl:1457
- +(x::Rational, y::Integer) in Base at rational.jl:385
- +(x::Rational, y::Rational) in Base at rational.jl:356
- +(x::Rational) in Base at rational.jl:342
- +(x::Real, z::Complex{Bool}) in Base at complex.jl:322
- +(x::Real, z::Complex) in Base at complex.jl:334
- +(y::AbstractFloat, x::Bool) in Base at bool.jl:178
- +(x::Base.TwicePrecision{T}, y::Base.TwicePrecision{T}) where T in Base at twiceprecision.jl:295
- +(x::Base.TwicePrecision, y::Base.TwicePrecision) in Base at twiceprecision.jl:300
- +(x::Base.TwicePrecision, y::Number) in Base at twiceprecision.jl:289
- +(A::LinearAlgebra.Tridiagonal, B::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/special.jl:256
- +(B::LinearAlgebra.Tridiagonal, A::LinearAlgebra.Bidiagonal) in LinearAlgebra
- +(B::LinearAlgebra.Tridiagonal, A::LinearAlgebra.Diagonal) in LinearAlgebra
- +(A::LinearAlgebra.Tridiagonal, B::LinearAlgebra.SymTridiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/special.jl:201
- +(x::LinearAlgebra.Tridiagonal, H::LinearAlgebra.UpperHessenberg) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/hessenberg.jl:135
- +(A::LinearAlgebra.Tridiagonal, B::LinearAlgebra.Tridiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/tridiag.jl:836
- +(H::LinearAlgebra.Hermitian, D::LinearAlgebra.Diagonal{var"#s4718", V} where {var"#s4718"<:Real, V<:AbstractVector{var"#s4718"}}) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/special.jl:306
- +(A::LinearAlgebra.Hermitian, J::LinearAlgebra.UniformScaling{<:Complex}) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/uniformscaling.jl:201
- +(A::LinearAlgebra.Hermitian, B::LinearAlgebra.SymTridiagonal{var"#s4715", V} where {var"#s4715"<:Real, V<:AbstractVector{var"#s4715"}}) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/symmetric.jl:699
- +(A::LinearAlgebra.Hermitian, B::LinearAlgebra.Symmetric{var"#s4718", S} where {var"#s4718"<:Real, S<:(AbstractMatrix{<:var"#s4718"})}) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/symmetric.jl:694
- +(A::LinearAlgebra.Hermitian, B::LinearAlgebra.Hermitian) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/symmetric.jl:686
- +(x::Dates.Instant) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/arithmetic.jl:4
- +(x::Ptr, y::Integer) in Base at pointer.jl:314
- +(A::BitArray, B::BitArray) in Base at bitarray.jl:1184
- +(x::Dates.AbstractTime, y::Missing) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/arithmetic.jl:90
- +(A::LinearAlgebra.SymTridiagonal, B::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/special.jl:261
- +(B::LinearAlgebra.SymTridiagonal, A::LinearAlgebra.Diagonal) in LinearAlgebra
- +(x::LinearAlgebra.SymTridiagonal, H::LinearAlgebra.UpperHessenberg) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/hessenberg.jl:135
- +(A::LinearAlgebra.SymTridiagonal, B::LinearAlgebra.Symmetric) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/symmetric.jl:696
- +(A::LinearAlgebra.SymTridiagonal{var"#s4716", V} where {var"#s4716"<:Real, V<:AbstractVector{var"#s4716"}}, B::LinearAlgebra.Hermitian) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/symmetric.jl:698
- +(B::LinearAlgebra.SymTridiagonal, A::LinearAlgebra.Bidiagonal) in LinearAlgebra
- +(B::LinearAlgebra.SymTridiagonal, A::LinearAlgebra.Tridiagonal) in LinearAlgebra
- +(A::LinearAlgebra.SymTridiagonal, B::LinearAlgebra.SymTridiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/tridiag.jl:209
- +(r::AbstractRange{<:Dates.TimeType}, x::Dates.Period) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/ranges.jl:65
- +(A::LinearAlgebra.UnitLowerTriangular, B::LinearAlgebra.Bidiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/special.jl:96
- +(UL::LinearAlgebra.UnitLowerTriangular, J::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/uniformscaling.jl:187
- +(A::LinearAlgebra.UnitLowerTriangular, B::LinearAlgebra.UnitLowerTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/triangular.jl:966
- +(A::LinearAlgebra.UnitLowerTriangular, B::LinearAlgebra.LowerTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/triangular.jl:958
- +(r1::LinRange{T}, r2::LinRange{T}) where T in Base at range.jl:1464
- +(x::T, y::Integer) where T<:AbstractChar in Base at char.jl:247
- +(x::AbstractIrrational, y::AbstractIrrational) in Base at irrationals.jl:199
- +(A::LinearAlgebra.UnitUpperTriangular, B::LinearAlgebra.UnitUpperTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/triangular.jl:962
- +(A::LinearAlgebra.UnitUpperTriangular, B::LinearAlgebra.UpperTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/triangular.jl:954
- +(A::LinearAlgebra.LowerTriangular, B::LinearAlgebra.UnitLowerTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/triangular.jl:950
- +(A::LinearAlgebra.LowerTriangular, B::LinearAlgebra.LowerTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/triangular.jl:942
- +(A::LinearAlgebra.UpperTriangular, B::LinearAlgebra.UnitUpperTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/triangular.jl:946
- +(A::LinearAlgebra.UpperTriangular, B::LinearAlgebra.UpperTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/triangular.jl:938
- +(A::Union{LinearAlgebra.LowerTriangular{T, S}, LinearAlgebra.UnitLowerTriangular{T, S}, LinearAlgebra.UnitUpperTriangular{T, S}, LinearAlgebra.UpperTriangular{T, S}} where {T, S}, B::Union{LinearAlgebra.LowerTriangular{T, S}, LinearAlgebra.UnitLowerTriangular{T, S}, LinearAlgebra.UnitUpperTriangular{T, S}, LinearAlgebra.UpperTriangular{T, S}} where {T, S}) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/triangular.jl:970
- +(A::LinearAlgebra.AbstractTriangular, B::LinearAlgebra.AbstractTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/triangular.jl:971
- +(H::LinearAlgebra.UpperHessenberg, x::LinearAlgebra.UpperTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/hessenberg.jl:134
- +(H::LinearAlgebra.UpperHessenberg, x::LinearAlgebra.Diagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/hessenberg.jl:134
- +(H::LinearAlgebra.UpperHessenberg, x::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/hessenberg.jl:134
- +(A::LinearAlgebra.UpperHessenberg, B::LinearAlgebra.UpperHessenberg) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/hessenberg.jl:127
- +(H::LinearAlgebra.UpperHessenberg, x::LinearAlgebra.UnitUpperTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/hessenberg.jl:134
- +(H::LinearAlgebra.UpperHessenberg, x::LinearAlgebra.Tridiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/hessenberg.jl:134
- +(H::LinearAlgebra.UpperHessenberg, x::LinearAlgebra.Bidiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/hessenberg.jl:134
- +(H::LinearAlgebra.UpperHessenberg, x::LinearAlgebra.SymTridiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/hessenberg.jl:134
- +(x::Number, J::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/uniformscaling.jl:148
- +(::Number, ::Missing) in Base at missing.jl:124
- +(x::Number, y::Base.TwicePrecision) in Base at twiceprecision.jl:293
- +(x::Number) in Base at operators.jl:579
- +(x::T, y::T) where T<:Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8} in Base at int.jl:87
- +(c::Union{UInt16, UInt32, UInt64, UInt8}, x::BigInt) in Base.GMP at gmp.jl:551
- +(c::Union{Int16, Int32, Int64, Int8}, x::BigInt) in Base.GMP at gmp.jl:557
- +(a::Integer, b::Integer) in Base at int.jl:1011
- +(x::T, y::T) where T<:Union{Float16, Float32, Float64} in Base at float.jl:495
- +(x::T, y::T) where T<:Number in Base at promotion.jl:623
- +(y::Integer, x::Rational) in Base at rational.jl:392
- +(c::Union{UInt16, UInt32, UInt64, UInt8}, x::BigFloat) in Base.MPFR at mpfr.jl:583
- +(c::Union{Int16, Int32, Int64, Int8}, x::BigFloat) in Base.MPFR at mpfr.jl:591
- +(c::Union{Float16, Float32, Float64}, x::BigFloat) in Base.MPFR at mpfr.jl:599
- +(x::Number, y::Number) in Base at promotion.jl:433
- +(index1::CartesianIndex{N}, index2::CartesianIndex{N}) where N in Base.IteratorsMD at multidimensional.jl:123
- +(index::CartesianIndex) in Base.IteratorsMD at multidimensional.jl:120
- +(r1::StepRangeLen{T, R}, r2::StepRangeLen{T, R}) where {R<:Base.TwicePrecision, T} in Base at twiceprecision.jl:625
- +(r1::StepRangeLen{T, S}, r2::StepRangeLen{T, S}) where {T, S} in Base at range.jl:1480
- +(D::LinearAlgebra.Diagonal{var"#s4718", V} where {var"#s4718"<:Real, V<:AbstractVector{var"#s4718"}}, H::LinearAlgebra.Hermitian) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/special.jl:302
- +(x::LinearAlgebra.Diagonal, H::LinearAlgebra.UpperHessenberg) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/hessenberg.jl:135
- +(D::LinearAlgebra.Diagonal{var"#s4718", V} where {var"#s4718"<:Number, V<:AbstractVector{var"#s4718"}}, S::LinearAlgebra.Symmetric) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/special.jl:294
- +(A::LinearAlgebra.Diagonal, B::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/special.jl:271
- +(Da::LinearAlgebra.Diagonal, Db::LinearAlgebra.Diagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/diagonal.jl:291
- +(A::LinearAlgebra.Diagonal, B::LinearAlgebra.Tridiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/special.jl:211
- +(B::LinearAlgebra.Diagonal, A::LinearAlgebra.Bidiagonal) in LinearAlgebra
- +(A::LinearAlgebra.Diagonal, B::LinearAlgebra.SymTridiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/special.jl:183
- +(S::LinearAlgebra.Symmetric, D::LinearAlgebra.Diagonal{var"#s4718", V} where {var"#s4718"<:Number, V<:AbstractVector{var"#s4718"}}) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/special.jl:298
- +(A::LinearAlgebra.Symmetric, B::LinearAlgebra.SymTridiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/symmetric.jl:697
- +(A::LinearAlgebra.Symmetric{var"#s4717", S} where {var"#s4717"<:Real, S<:(AbstractMatrix{<:var"#s4717"})}, B::LinearAlgebra.Hermitian) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/symmetric.jl:695
- +(A::LinearAlgebra.Symmetric, B::LinearAlgebra.Symmetric) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/symmetric.jl:686
- +(A::LinearAlgebra.UnitUpperTriangular, B::LinearAlgebra.Bidiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/special.jl:96
- +(x::LinearAlgebra.UnitUpperTriangular, H::LinearAlgebra.UpperHessenberg) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/hessenberg.jl:135
- +(UL::LinearAlgebra.UnitUpperTriangular, J::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/uniformscaling.jl:187
- +(x::Integer, y::AbstractChar) in Base at char.jl:257
- +(x::Integer, y::Ptr) in Base at pointer.jl:316
- +(x::LinearAlgebra.Bidiagonal, H::LinearAlgebra.UpperHessenberg) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/hessenberg.jl:135
- +(A::LinearAlgebra.Bidiagonal, B::LinearAlgebra.LowerTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/special.jl:104
- +(A::LinearAlgebra.Bidiagonal, B::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/special.jl:266
- +(A::LinearAlgebra.Bidiagonal, B::LinearAlgebra.SymTridiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/special.jl:241
- +(A::LinearAlgebra.Bidiagonal, B::LinearAlgebra.UpperTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/special.jl:104
- +(A::LinearAlgebra.Bidiagonal, B::LinearAlgebra.Diagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/special.jl:156
- +(A::LinearAlgebra.Bidiagonal, B::LinearAlgebra.UnitLowerTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/special.jl:104
- +(A::LinearAlgebra.Bidiagonal, B::LinearAlgebra.UnitUpperTriangular) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/special.jl:104
- +(A::LinearAlgebra.Bidiagonal, B::LinearAlgebra.Bidiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/bidiag.jl:418
- +(A::LinearAlgebra.Bidiagonal, B::LinearAlgebra.Tridiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/special.jl:226
- +(F::LinearAlgebra.Hessenberg, J::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/hessenberg.jl:579
- +(A::LinearAlgebra.LowerTriangular, B::LinearAlgebra.Bidiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/special.jl:96
- +(J::LinearAlgebra.UniformScaling, B::BitMatrix) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/uniformscaling.jl:155
- +(x::LinearAlgebra.UniformScaling, H::LinearAlgebra.UpperHessenberg) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/hessenberg.jl:135
- +(J1::LinearAlgebra.UniformScaling, J2::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/uniformscaling.jl:153
- +(B::LinearAlgebra.UniformScaling, A::LinearAlgebra.SymTridiagonal) in LinearAlgebra
- +(J::LinearAlgebra.UniformScaling, F::LinearAlgebra.Hessenberg) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/hessenberg.jl:580
- +(B::LinearAlgebra.UniformScaling, A::LinearAlgebra.Diagonal) in LinearAlgebra
- +(B::LinearAlgebra.UniformScaling, A::LinearAlgebra.Tridiagonal) in LinearAlgebra
- +(B::LinearAlgebra.UniformScaling, A::LinearAlgebra.Bidiagonal) in LinearAlgebra
- +(J::LinearAlgebra.UniformScaling, A::AbstractMatrix) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/uniformscaling.jl:156
- +(J::LinearAlgebra.UniformScaling, x::Number) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/uniformscaling.jl:147
- +(J::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/uniformscaling.jl:152
- +(a::Dates.TimeType, b::Dates.Period, c::Dates.Period) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/periods.jl:354
- +(a::Dates.TimeType, b::Dates.Period, c::Dates.Period, d::Dates.Period...) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/periods.jl:355
- +(x::Dates.TimeType, y::Dates.CompoundPeriod) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/periods.jl:357
- +(x::Dates.TimeType) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/arithmetic.jl:8
- +(y::Dates.TimeType, x::StridedArray{<:Union{Dates.CompoundPeriod, Dates.Period}}) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/deprecated.jl:18
- +(x::P, y::P) where P<:Dates.Period in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/periods.jl:75
- +(x::Dates.Period, y::Dates.Period) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/periods.jl:332
- +(y::Dates.Period, x::Dates.CompoundPeriod) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/periods.jl:334
- +(y::Dates.Period, x::Dates.TimeType) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/arithmetic.jl:87
- +(x::Dates.Period, r::AbstractRange{<:Dates.TimeType}) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/ranges.jl:64
- +(A::LinearAlgebra.UpperTriangular, B::LinearAlgebra.Bidiagonal) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/special.jl:96
- +(x::LinearAlgebra.UpperTriangular, H::LinearAlgebra.UpperHessenberg) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/hessenberg.jl:135
- +(x::AbstractArray{<:Dates.TimeType}, y::Union{Dates.CompoundPeriod, Dates.Period}) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/deprecated.jl:6
- +(A::AbstractMatrix, J::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/share/julia/stdlib/v1.12/LinearAlgebra/src/uniformscaling.jl:220
- +(r1::Union{LinRange, OrdinalRange, StepRangeLen}, r2::Union{LinRange, OrdinalRange, StepRangeLen}) in Base at range.jl:1473
- +(X::StridedArray{<:Union{Dates.CompoundPeriod, Dates.Period}}, Y::StridedArray{<:Union{Dates.CompoundPeriod, Dates.Period}}) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/deprecated.jl:62
- +(A::AbstractArray, B::AbstractArray) in Base at arraymath.jl:6
- +(x::StridedArray{<:Union{Dates.CompoundPeriod, Dates.Period}}) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/deprecated.jl:55
- +(x::AbstractArray{<:Number}) in Base at abstractarraymath.jl:285
- +(x::Union{Dates.CompoundPeriod, Dates.Period}) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/periods.jl:343
- +(x::StridedArray{<:Union{Dates.CompoundPeriod, Dates.Period}}, y::Dates.TimeType) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/deprecated.jl:10
- +(y::Union{Dates.CompoundPeriod, Dates.Period}, x::AbstractArray{<:Dates.TimeType}) in Dates at /usr/share/julia/stdlib/v1.12/Dates/src/deprecated.jl:14
- +(a, b, c, xs...) in Base at operators.jl:642
Každá z těchto 196 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 * xf (generic function with 2 methods)
f(1) # 1 "je" jak Integer tak Number a Integer <: Number2
Int64 <: Numbertrue
Int64 <: Integertrue
Integer <: Numbertrue
f(1.0) # 1.0 "je" jen Number3.0
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 * yf (generic function with 4 methods)
f(1.0, 2)3.0
f(1, 2.0)2.0
@which f(1.0, 2)@which f(1, 2.0)f(1.0, 2.0)MethodError: f(::Float64, ::Float64) is ambiguous.
Candidates:
f(x, y::Float64)
@ Main In[9]:2
f(x::Float64, y)
@ Main In[9]:1
Possible fix, define
f(::Float64, ::Float64)
Stacktrace:
[1] top-level scope
@ In[14]:1
[2] eval(m::Module, e::Any)
@ Core ./boot.jl:489Funkce f má aktuálně čtyři metody:
methods(f)- f(x, y::Float64) in Main at In[9]:2
- f(x::Float64, y) in Main at In[9]:1
- f(x::Integer) in Main at In[3]:1
- f(x::Number) in Main at In[3]:2
Možná oprava spočívá v dodefinování chybějící metody:
f(x::Float64, y::Float64) = x - yf (generic function with 5 methods)
f(1.0, 2.0)-1.0
methods(f)- f(x::Float64, y::Float64) in Main at In[16]:1
- f(x, y::Float64) in Main at In[9]:2
- f(x::Float64, y) in Main at In[9]:1
- f(x::Integer) in Main at In[3]:1
- f(x::Number) in Main at In[3]:2
f(1, 2.0)2.0
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
endsame_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, String)DataType
true
methods(same_type)- same_type(x::T, y::T) where T in Main at In[20]:1
- same_type(x, y) in Main at In[20]: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
endparametric_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. Kód `MyRational`, LaTeX ``\\frac{1}{2}``.
"""
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
endMyRational
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.
?MyRationalsearch: MyRational Rational Irrational
MyRational{T <: Integer} <: Number
My home made rational number. Kód MyRational, LaTeX .
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:
p = MyRational(1, 2)
q = MyRational(2, 3)
p + q+ not defined for MyRational{Int64}
Stacktrace:
[1] error(::String, ::String, ::Type)
@ Base ./error.jl:54
[2] no_op_err(name::String, T::Type)
@ Base ./promotion.jl:622
[3] +(x::MyRational{Int64}, y::MyRational{Int64})
@ Base ./promotion.jl:623
[4] top-level scope
@ In[29]:4
[5] eval(m::Module, e::Any)
@ Core ./boot.jl:489import 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 190 methods)
Pojďme ji hned s nadšením otestovat.
p = MyRational(1, 2)
q = MyRational(2, 3)
p + qMyRational{Int64}(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 + q7/6
MyRational(2, -3)-2/3
Pokud chceme hezký LaTeX výstup, pak musíme dodefinovat následující metodu.
function Base.show(io::IO, ::MIME"text/latex", q::MyRational{T}) where { T <: Integer }
if q.den == one(T)
print(io, "\\begin{equation*}$(q.num)\\end{equation*}")
elseif q.num < zero(T)
print(io, "\\begin{equation*}-\\frac{$(-q.num)}{$(q.den)}\\end{equation*}")
else
print(io, "\\begin{equation*}\\frac{$(q.num)}{$(q.den)}\\end{equation*}")
end
endMyRational(25, 100)MyRational(-24, 40)MyRational(0, 1)MyRational(2, 2)MyRational(-1, 1)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 195 methods)
Otestujme správnou funkčnost algebraických operací mezi našimi racionálními čísly:
println("p = ", p, ", q = ", q)
p * qp = 1/2, q = 2/3
-pp - qp ^ 5@which p ^ 5p ^ (-2)MethodError: no method matching MyRational{Int64}(::Int64)
The type `MyRational{Int64}` exists, but no method is defined for this combination of argument types when trying to construct it.
Closest candidates are:
(::Type{T})(::T) where T<:Number
@ Core boot.jl:965
(::Type{T})(::Base.TwicePrecision) where T<:Number
@ Base twiceprecision.jl:265
(::Type{T})(::AbstractChar) where T<:Union{AbstractChar, Number}
@ Base char.jl:52
Stacktrace:
[1] convert(::Type{MyRational{Int64}}, x::Int64)
@ Base ./number.jl:7
[2] one(::Type{MyRational{Int64}})
@ Base ./number.jl:355
[3] one(x::MyRational{Int64})
@ Base ./number.jl:356
[4] inv(x::MyRational{Int64})
@ Base ./number.jl:255
[5] literal_pow(f::typeof(^), x::MyRational{Int64}, ::Val{-2})
@ Base ./intfuncs.jl:441
[6] top-level scope
@ In[47]:1
[7] eval(m::Module, e::Any)
@ Core ./boot.jl:4891 / ppromotion of types Int64 and MyRational{Int64} failed to change any arguments
Stacktrace:
[1] error(::String, ::String, ::String)
@ Base ./error.jl:54
[2] sametype_error(input::Tuple{Int64, MyRational{Int64}})
@ Base ./promotion.jl:428
[3] not_sametype(x::Tuple{Int64, MyRational{Int64}}, y::Tuple{Int64, MyRational{Int64}})
@ Base ./promotion.jl:422
[4] promote
@ ./promotion.jl:405 [inlined]
[5] /(x::Int64, y::MyRational{Int64})
@ Base ./promotion.jl:436
[6] top-level scope
@ In[48]:1
[7] eval(m::Module, e::Any)
@ Core ./boot.jl:489Cvič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@which p ^ 3p ^ (-1)MethodError: no method matching MyRational{Int64}(::Int64)
The type `MyRational{Int64}` exists, but no method is defined for this combination of argument types when trying to construct it.
Closest candidates are:
(::Type{T})(::T) where T<:Number
@ Core boot.jl:965
(::Type{T})(::Base.TwicePrecision) where T<:Number
@ Base twiceprecision.jl:265
(::Type{T})(::AbstractChar) where T<:Union{AbstractChar, Number}
@ Base char.jl:52
Stacktrace:
[1] convert(::Type{MyRational{Int64}}, x::Int64)
@ Base ./number.jl:7
[2] one(::Type{MyRational{Int64}})
@ Base ./number.jl:355
[3] one(x::MyRational{Int64})
@ Base ./number.jl:356
[4] inv(x::MyRational{Int64})
@ Base ./number.jl:255
[5] literal_pow(f::typeof(^), x::MyRational{Int64}, ::Val{-1})
@ Base ./intfuncs.jl:441
[6] top-level scope
@ In[51]:1
[7] eval(m::Module, e::Any)
@ Core ./boot.jl:489MyRational{T}(n::T) where { T <: Integer } = MyRational(n, one(1))@which one(1)p ^ (-1)/ not defined for MyRational{Int64}
Stacktrace:
[1] error(::String, ::String, ::Type)
@ Base ./error.jl:54
[2] no_op_err(name::String, T::Type)
@ Base ./promotion.jl:622
[3] /(x::MyRational{Int64}, y::MyRational{Int64})
@ Base ./promotion.jl:626
[4] inv(x::MyRational{Int64})
@ Base ./number.jl:255
[5] literal_pow(f::typeof(^), x::MyRational{Int64}, ::Val{-1})
@ Base ./intfuncs.jl:441
[6] top-level scope
@ In[54]:1
[7] eval(m::Module, e::Any)
@ Core ./boot.jl:489Base.:/(p::MyRational{T}, q::MyRational{T}) where { T <: Integer } = MyRational(p.num * q.den, p.den * q.num)p ^ (-1)MyRational(1, 0) / MyRational(0, 1)Zero denominator is forbidden by god! Stacktrace: [1] error(s::String) @ Base ./error.jl:44 [2] MyRational(num::Int64, den::Int64) @ Main ./In[27]:12 [3] top-level scope @ In[57]:1 [4] eval(m::Module, e::Any) @ Core ./boot.jl:489
Base.inv(q::MyRational{T}) where { T <: Integer } = MyRational(q.den, q.num)inv(MyRational(3, 5))-inv(MyRational(-3, 5))p ^ (-2)p ^ (-1)inv(MyRational(0, 1))Zero denominator is forbidden by god!
Stacktrace:
[1] error(s::String)
@ Base ./error.jl:44
[2] MyRational(num::Int64, den::Int64)
@ Main ./In[27]:12
[3] inv(q::MyRational{Int64})
@ Main ./In[58]:1
[4] top-level scope
@ In[63]:1
[5] eval(m::Module, e::Any)
@ Core ./boot.jl:489Racioná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 // 32//3
4 // 22//1
q = 3 // 23//2
Poznámka: Hezčí výpis.
function Base.show(io::IO, ::MIME"text/latex", q::Rational{T}) where { T <: Integer }
if q.num < 0
print(io, "\\begin{equation*}-\\frac{$(-q.num)}{$(q.den)}\\end{equation*}")
elseif q.num == 0
print(io, "\\begin{equation*}0\\end{equation*}")
else
print(io, "\\begin{equation*}\\frac{$(q.num)}{$(q.den)}\\end{equation*}")
end
end2 // 3@which Rational(1, 2)Poznámka: Julia Rational umí vytvořit exaktní reprezentaci strojového čísla (které z definice je vždy racionální číslo):
x = Rational(0.3)x.num / x.den0.3
Rational(0.3) - 3 // 10Float64(Rational(0.3) - 3 // 10)-1.1102230246251566e-17
Rational(0.5)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, "ı")
endz = MyComplex(1, 2)1 + 2ı
z = MyComplex(-1, 2)-1 + 2ı
z = MyComplex(1, -2)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/ (generic function with 132 methods)
w = MyComplex(3, 4)3 + 4ı
z1 - 2ı
z + wz * winv(z) * MyComplex(1.0, -2.0)MethodError: no method matching *(::Nothing, ::MyComplex{Float64})
The function `*` exists, but no method is defined for this combination of argument types.
Closest candidates are:
*(::Any, ::Any, ::Any, ::Any...)
@ Base operators.jl:642
*(::Missing, ::Number)
@ Base missing.jl:123
*(::Base.TwicePrecision, ::Number)
@ Base twiceprecision.jl:306
...
Stacktrace:
[1] top-level scope
@ In[84]:1
[2] eval(m::Module, e::Any)
@ Core ./boot.jl:489MyComplex(1.0, -2.0) / MyComplex(1.0, -2.0)z / zCvič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::Integer}(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))")ArgumentError: Package Primes not found in current path.
- Run `import Pkg; Pkg.add("Primes")` to install the Primes package.
Stacktrace:
[1] macro expansion
@ ./loading.jl:2375 [inlined]
[2] macro expansion
@ ./lock.jl:376 [inlined]
[3] __require(into::Module, mod::Symbol)
@ Base ./loading.jl:2358
[4] require(into::Module, mod::Symbol)
@ Base ./loading.jl:2334
[5] eval(m::Module, e::Any)
@ Core ./boot.jl:489Následuje několik ukázek použití.
a = MMG(3, 11); b = MMG(5, 11); c = MMG(5, 13)UndefVarError: `MMG` not defined in `Main` Suggestion: check for spelling errors or missing imports. Stacktrace: [1] top-level scope @ In[88]:1 [2] eval(m::Module, e::Any) @ Core ./boot.jl:489
typeof(a)UndefVarError: `a` not defined in `Main` Suggestion: add an appropriate import or assignment. This global was declared but not assigned. Stacktrace: [1] top-level scope @ In[89]:1 [2] eval(m::Module, e::Any) @ Core ./boot.jl:489
modulus(a)UndefVarError: `modulus` not defined in `Main` Suggestion: check for spelling errors or missing imports. Stacktrace: [1] top-level scope @ In[90]:1 [2] eval(m::Module, e::Any) @ Core ./boot.jl:489
a * bUndefVarError: `a` not defined in `Main` Suggestion: add an appropriate import or assignment. This global was declared but not assigned. Stacktrace: [1] top-level scope @ In[91]:1 [2] eval(m::Module, e::Any) @ Core ./boot.jl:489
# toto by šlo ještě vylepšit..., výchozí chování.
b * cUndefVarError: `b` not defined in `Main` Suggestion: check for spelling errors or missing imports. Stacktrace: [1] top-level scope @ In[92]:2 [2] eval(m::Module, e::Any) @ Core ./boot.jl:489
inv(a)UndefVarError: `a` not defined in `Main` Suggestion: add an appropriate import or assignment. This global was declared but not assigned. Stacktrace: [1] top-level scope @ In[93]:1 [2] eval(m::Module, e::Any) @ Core ./boot.jl:489
a * inv(a)UndefVarError: `a` not defined in `Main` Suggestion: add an appropriate import or assignment. This global was declared but not assigned. Stacktrace: [1] top-level scope @ In[94]:1 [2] eval(m::Module, e::Any) @ Core ./boot.jl:489
c * inv(c)UndefVarError: `c` not defined in `Main` Suggestion: check for spelling errors or missing imports. Stacktrace: [1] top-level scope @ In[95]:1 [2] eval(m::Module, e::Any) @ Core ./boot.jl:489
inv(c)UndefVarError: `c` not defined in `Main` Suggestion: check for spelling errors or missing imports. Stacktrace: [1] top-level scope @ In[96]:1 [2] eval(m::Module, e::Any) @ Core ./boot.jl:489
b ^ 123UndefVarError: `b` not defined in `Main` Suggestion: check for spelling errors or missing imports. Stacktrace: [1] top-level scope @ In[97]:1 [2] eval(m::Module, e::Any) @ Core ./boot.jl:489
MMG(0, 3)UndefVarError: `MMG` not defined in `Main` Suggestion: check for spelling errors or missing imports. Stacktrace: [1] top-level scope @ In[98]:1 [2] eval(m::Module, e::Any) @ Core ./boot.jl:489
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) The function `F` exists, but no method is defined for this combination of argument types. Closest candidates are: F(::Int64) @ Main In[99]:1 Stacktrace: [1] top-level scope @ In[102]:1 [2] eval(m::Module, e::Any) @ Core ./boot.jl:489
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 + yh (generic function with 2 methods)
h(1)3
h(1, 1)2
methods(h)- h(x, y) in Main at In[103]:1
- h(x) in Main at In[103]:1
I v tomto zápisu lze případně anotovat typ proměnné y pomocí ::.
gg(x, y::Int64=4) = x * ygg (generic function with 2 methods)
gg(1, 2.5)MethodError: no method matching gg(::Int64, ::Float64) The function `gg` exists, but no method is defined for this combination of argument types. Closest candidates are: gg(::Any, ::Int64) @ Main In[107]:1 gg(::Any) @ Main In[107]:1 Stacktrace: [1] top-level scope @ In[108]:1 [2] eval(m::Module, e::Any) @ Core ./boot.jl:489
ch(x, y=2, z=4) = x ^ (y ^ z)ch (generic function with 3 methods)
methods(ch)- ch(x, y, z) in Main at In[109]:1
- ch(x, y) in Main at In[109]:1
- ch(x) in Main at In[109]:1
ch(2, 2, 3)256
ch(2, 1)2
ch(2)65536
Operátor ..., "varargs"
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 + zg (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})
The function `g` exists, but no method is defined for this combination of argument types.
Closest candidates are:
g(::Any, ::Any, ::Any)
@ Main In[114]:1
Stacktrace:
[1] top-level scope
@ In[117]:1
[2] eval(m::Module, e::Any)
@ Core ./boot.jl:489Operá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)()
()()
typeof(())Tuple{}(,) # <- blbostParseError: # Error @ In[122]:1:2 (,) # <- blbost #└ ── Expected `)` or `,` Stacktrace: [1] top-level scope @ In[122]:1 [2] eval(m::Module, e::Any) @ Core ./boot.jl:489
(1)1
typeof((1))Int64
(1,)(1,)
typeof((1,))Tuple{Int64}H(1, 2)(2,)
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!
vcat([1,2], [3,4], [4,5])6-element Vector{Int64}:
1
2
3
4
4
5Argumenty tvaru keyword=value (keyword arguments, "kwargs")
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
Lze i se středníkem za pozičními argumenty:
G(2, 3; operator=/)0.6666666666666666
G(2, 3, *)MethodError: no method matching G(::Int64, ::Int64, ::typeof(*)) The function `G` exists, but no method is defined for this combination of argument types. Closest candidates are: G(::Any, ::Any; operator) @ Main In[134]:1 Stacktrace: [1] top-level scope @ In[138]:1 [2] eval(m::Module, e::Any) @ Core ./boot.jl:489
U(x; y) = x + yU (generic function with 1 method)
U(1)UndefKeywordError: keyword argument `y` not assigned Stacktrace: [1] U(x::Int64) @ Main ./In[139]:1 [2] top-level scope @ In[140]:1 [3] eval(m::Module, e::Any) @ Core ./boot.jl:489
U(1, y=42)43
U(1, 2)MethodError: no method matching U(::Int64, ::Int64) The function `U` exists, but no method is defined for this combination of argument types. Closest candidates are: U(::Any; y) @ Main In[139]:1 Stacktrace: [1] top-level scope @ In[142]:1 [2] eval(m::Module, e::Any) @ Core ./boot.jl:489
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:
function J(args...; kwargs...)
println(length(args))
println(args)
println(kwargs)
endJ (generic function with 1 method)
methods(J)- J(args...; kwargs...) in Main at In[143]:1
J(1, 2, a=1, b="x")2
(1, 2)
Base.Pairs{Symbol, Any, Nothing, @NamedTuple{a::Int64, b::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(keys(kwargs))
println(kwargs[:a])
endkwargs_example (generic function with 1 method)
kwargs_example(a=1)(:a,) 1
kwargs_example(b=10)(:b,)
FieldError: type NamedTuple has no field `a`, available fields: `b`
Stacktrace:
[1] getindex
@ ./namedtuple.jl:166 [inlined]
[2] getindex
@ ./iterators.jl:313 [inlined]
[3] kwargs_example(; kwargs::@Kwargs{b::Int64})
@ Main ./In[146]:3
[4] top-level scope
@ In[148]:1
[5] eval(m::Module, e::Any)
@ Core ./boot.jl:489function křoví(x)
return x + 1
endkřoví (generic function with 1 method)
Poziční argumenty nelze předávat pomocí jejich názvu (klíče).
křoví(x=1)MethodError: no method matching křoví(; x::Int64) The function `křoví` exists, but no method is defined for this combination of argument types. Closest candidates are: křoví(::Any) got unsupported keyword argument "x" @ Main In[149]:1 Stacktrace: [1] top-level scope @ In[150]:1 [2] eval(m::Module, e::Any) @ Core ./boot.jl:489
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 (generic function with 1 method)
func(-10)1
func(3)3
func(15)42
Pomocí tuplů můžeme vracet i "více" hodnot.
function func_tuple(x)
return (x, x^2)
endfunc_tuple (generic function with 1 method)
Pak je vhodné výsledek rovnou přiřadit do dvou proměnných:
a, b = func_tuple(10)(10, 100)
a10
b100
Nebo můžeme samozřejmě přijmout celý tuple.
c = func_tuple(10)
c(10, 100)
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
typeof(ex.head)Symbol
ex.args # argumenty3-element Vector{Any}:
:+
1
2Př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 == myextrue
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.
dump(ex)Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol +
2: Int64 1
3: Int64 2
eval(ex)3
eval(myex)3
ex2 = Expr(:call, :neznámá_věc, 1, 2):(neznámá_věc(1, 2))
dump(ex2)Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol neznámá_věc
2: Int64 1
3: Int64 2
eval(ex2)UndefVarError: `neznámá_věc` not defined in `Main` Suggestion: check for spelling errors or missing imports. Stacktrace: [1] top-level scope @ none:1 [2] eval(m::Module, e::Any) @ Core ./boot.jl:489 [3] top-level scope @ In[175]:1 [4] eval(m::Module, e::Any) @ Core ./boot.jl:489
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
endquote
#= In[180]:2 =#
for j = 1:10
#= In[180]:3 =#
println(j)
#= In[180]:4 =#
end
endtypeof(ex)Expr
dump(ex)Expr
head: Symbol block
args: Array{Any}((2,))
1: LineNumberNode
line: Int64 2
file: Symbol In[180]
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[180]
2: Expr
head: Symbol call
args: Array{Any}((2,))
1: Symbol println
2: Symbol j
3: LineNumberNode
line: Int64 4
file: Symbol In[180]
eval(ex)1 2 3 4 5 6 7 8 9 10
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.5)))Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol f
2: Int64 1
3: Float64 2.5
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
@show_args f(x, z)Arguments: x = 42 z = 1 - 2ı
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 = 4242
@zerox0
x42
@zerox20
x0
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.
2.2 @which
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.0g1(x::Int64) = x + 1
g1(x::Float64) = x - 1g1 (generic function with 2 methods)
@which g1(1.0)@which g1(1)Podobně se chová makro @edit, které rovnou otevře zdrojový kód.
2.3 @debug, @info, @warn, @error
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[208]:1
@error "Unable to compute!"┌ Error: Unable to compute! └ @ Main In[209]:1
2.4 @time, @timed a @timev
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.085482243876011
0.34225130247656066
0.522933872303889
0.54122846266418
0.04360293938134674
0.21030818883786218
0.9335635192445657
0.5935772758399958
0.7934506732334682
0.19507185470977706@time sort(a)1.045570 seconds (1.16 M allocations: 59.744 MiB, 99.99% compilation time)
1000-element Vector{Float64}:
0.001360863470650786
0.0020107692081845485
0.002443249747673515
0.0027029987551763224
0.0027592738875369394
0.002825468923344565
0.003359929438204068
0.0036811734843921196
0.004319192506460512
0.009548623297789494
0.010200378265370458
0.01050806458229503
0.011268566857644213
⋮
0.9889572350603392
0.9894482662226048
0.9904801969097379
0.9906663899028898
0.9921897802411525
0.9923849374029236
0.9931027738918186
0.9936380717617321
0.9941918386279127
0.9942036095898867
0.994865417836923
0.9960126142183662d jako dictionary:
@timed sort(a)(value = [0.001360863470650786, 0.0020107692081845485, 0.002443249747673515, 0.0027029987551763224, 0.0027592738875369394, 0.002825468923344565, 0.003359929438204068, 0.0036811734843921196, 0.004319192506460512, 0.009548623297789494 … 0.9904801969097379, 0.9906663899028898, 0.9921897802411525, 0.9923849374029236, 0.9931027738918186, 0.9936380717617321, 0.9941918386279127, 0.9942036095898867, 0.994865417836923, 0.9960126142183662], time = 2.8334e-5, bytes = 18328, gctime = 0.0, gcstats = Base.GC_Diff(18328, 3, 0, 6, 0, 0, 0, 0, 0), lock_conflicts = 0, compile_time = 0.0, recompile_time = 0.0)
v jako verbose, čili podrobnější:
@timev sort(a)0.000054 seconds (9 allocations: 17.898 KiB) elapsed time (ns): 53643.0 gc time (ns): 0 bytes allocated: 18328 pool allocs: 6 non-pool GC allocs: 0 malloc() calls: 3 free() calls: 0 minor collections: 0 full collections: 0
1000-element Vector{Float64}:
0.001360863470650786
0.0020107692081845485
0.002443249747673515
0.0027029987551763224
0.0027592738875369394
0.002825468923344565
0.003359929438204068
0.0036811734843921196
0.004319192506460512
0.009548623297789494
0.010200378265370458
0.01050806458229503
0.011268566857644213
⋮
0.9889572350603392
0.9894482662226048
0.9904801969097379
0.9906663899028898
0.9921897802411525
0.9923849374029236
0.9931027738918186
0.9936380717617321
0.9941918386279127
0.9942036095898867
0.994865417836923
0.99601261421836622.5 @inbounds a @simd
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
4a[2]2
a[5]BoundsError: attempt to access 4-element Vector{Int64} at index [5]
Stacktrace:
[1] throw_boundserror(A::Vector{Int64}, I::Tuple{Int64})
@ Base ./essentials.jl:15
[2] getindex(A::Vector{Int64}, i::Int64)
@ Base ./essentials.jl:919
[3] top-level scope
@ In[217]:1
[4] eval(m::Module, e::Any)
@ Core ./boot.jl:489using BenchmarkToolsfunction f1(a::Vector{Float64}, n)
val = 0.0
for j = 1:n
val += a[j]
end
return val
end
function f2(a::Vector{Float64}, n)
val = 0.0
@simd for j = 1:n
@inbounds val += a[j]
end
return val
end
function f3(a::Vector{Float64}, n)
val = 0.0
for j = 1:n
@inbounds val += a[j]
end
return val
endf3 (generic function with 1 method)
a = rand(10^8);@benchmark f1($a, 10^6)BenchmarkTools.Trial: 3625 samples with 1 evaluation per sample. Range (min … max): 1.206 ms … 3.448 ms ┊ GC (min … max): 0.00% … 0.00% Time (median): 1.264 ms ┊ GC (median): 0.00% Time (mean ± σ): 1.368 ms ± 249.821 μs ┊ GC (mean ± σ): 0.00% ± 0.00% █▄▇▅▄▄▃▃▂▃▂▂▂▂▁▁▁ ▁ ▁ ████████████████████▇████▇▇█▇▆█▆▆▆▆▆▄▆▆▅▆▅▅▆▅▅▃▅▅▅▃▅▅▆▆▅▅▄▃ █ 1.21 ms Histogram: log(frequency) by time 2.41 ms < Memory estimate: 0 bytes, allocs estimate: 0.
@benchmark f2($a, 10^6)BenchmarkTools.Trial: 10000 samples with 1 evaluation per sample.
Range (min … max): 255.080 μs … 1.783 ms ┊ GC (min … max): 0.00% … 0.00%
Time (median): 332.019 μs ┊ GC (median): 0.00%
Time (mean ± σ): 369.133 μs ± 126.245 μs ┊ GC (mean ± σ): 0.00% ± 0.00%
▂▆█▄
▂▃▆████▇▅▅▅▄▄▃▃▃▃▃▃▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▁▂▂▂▂▂▂▂ ▃
255 μs Histogram: frequency by time 1.04 ms <
Memory estimate: 0 bytes, allocs estimate: 0.@benchmark f3($a, 10^6)BenchmarkTools.Trial: 3726 samples with 1 evaluation per sample. Range (min … max): 1.205 ms … 2.607 ms ┊ GC (min … max): 0.00% … 0.00% Time (median): 1.253 ms ┊ GC (median): 0.00% Time (mean ± σ): 1.336 ms ± 195.102 μs ┊ GC (mean ± σ): 0.00% ± 0.00% █▂█▅▄▄▃▂▃▂▂▂▁▂▂▁▁▁▁ ▁ ▁ ████████████████████████▇▇█▇▇█▇▆▆▅▇▆▅▅▅▅▅▃▅▆▅▅▆▅▅▅▅▃▄▅▃▄▄▅▃ █ 1.21 ms Histogram: log(frequency) by time 2.2 ms < Memory estimate: 0 bytes, allocs estimate: 0.
2.6 @test a @testset
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
endTest Summary: | Pass Total Time isodd method | 2 2 0.4s
Test.DefaultTestSet("isodd method", Any[], 2, false, false, true, 1.760610838489664e9, 1.760610838844445e9, false, "In[224]", Random.Xoshiro(0x3e83e50e33d7cf4d, 0x02a01a222f078fd6, 0xb03e7d30effa0837, 0x67433a04ac5c43a2, 0x8a733b8a27568150))Dále máme k dispozici @test_throws pro testování vyvolání výjimky.
2.6 @code_native
a.k.a "We need to go deeper..."
function g(x::Int64)
return x + 1
endg (generic function with 2 methods)
@code_native g(2) .text
.file "g"
.section .ltext,"axl",@progbits
.globl julia_g_17786 # -- Begin function julia_g_17786
.p2align 4, 0x90
.type julia_g_17786,@function
julia_g_17786: # @julia_g_17786
; Function Signature: g(Int64)
; ┌ @ In[225]:1 within `g`
# %bb.0: # %top
#DEBUG_VALUE: g:x <- $rdi
push rbp
mov rbp, rsp
; │ @ In[225]:2 within `g`
; │┌ @ int.jl:87 within `+`
lea rax, [rdi + 1]
; │└
pop rbp
ret
.Lfunc_end0:
.size julia_g_17786, .Lfunc_end0-julia_g_17786
; └
# -- End function
.section ".note.GNU-stack","",@progbits
2.7 ProgressMeter.jl
using ProgressMeter@showprogress for i in 1:50
sleep(0.1)
endProgress: 100%|█████████████████████████████████████████| Time: 0:00:06
@showprogress dt=1 desc="Computing..." for i in 1:50
sleep(0.1)
endComputing... 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ě nkrát.
macro dotimes(n, body)
quote
for i = 1:$n
$body
end
end
end@dotimes (macro with 1 method)
@dotimes 2 println("Hi!")Hi! Hi!
body = 1
n = 3
@dotimes 2 println("Hi!")Hi! Hi!
body1
n3
Cvičení: generování kódu
Definujme vlastní "číselný" typ:
struct MyNumber <: Number
value::Float64
endVygenerujte 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))))
endsin(MyNumber(0.3))MyNumber(0.29552020666133955)
cos(MyNumber(0.3))MyNumber(0.955336489125606)
exp(MyNumber(0.3))MyNumber(1.3498588075760032)
log(MyNumber(0.3))MyNumber(-1.2039728043259361)
Cvičení: Sledování průběhu for cyklu
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 (macro with 1 method)
@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 (generic function with 36 methods)
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)ArgumentError: Package Primes not found in current path.
- Run `import Pkg; Pkg.add("Primes")` to install the Primes package.
Stacktrace:
[1] macro expansion
@ ./loading.jl:2375 [inlined]
[2] macro expansion
@ ./lock.jl:376 [inlined]
[3] __require(into::Module, mod::Symbol)
@ Base ./loading.jl:2358
[4] require(into::Module, mod::Symbol)
@ Base ./loading.jl:2334
[5] eval(m::Module, e::Any)
@ Core ./boot.jl:489Makro dotimes.
macro dotimes(n, body)
quote
for i = 1:$n
$body
end
end
end@dotimes (macro with 1 method)
Průběh for cyklu.
macro progress(expr)
push!(expr.args[2].args, :(println(j)))
return expr
end@progress (macro with 1 method)