Параметрические типы
Параметрические типы — шаблоны вроде Vector{Int}, которые дают и обобщённость, и скорость.
Параметрический тип — это тип-шаблон с параметром в фигурных скобках, например
Vector{T}, гдеT— тип элементов.
Что значат фигурные скобки
Вы уже встречали записи вроде Vector{Int64} или Complex{Float64}. Часть в фигурных скобках — это параметр типа. Vector{Int64} и Vector{Float64} — два разных конкретных типа, порождённых одним шаблоном Vector{T}.
a = [1, 2, 3]
b = [1.0, 2.0, 3.0]
println(typeof(a))
println(typeof(b))
println(eltype(a)) # тип элементовВывод:
Vector{Int64}
Vector{Float64}
Int64Зачем это нужно
Параметрические типы решают противоречие между гибкостью и скоростью. С одной стороны, можно написать обобщённый код, работающий с массивом любого типа элементов. С другой — для каждого конкретного T компилятор знает точный размер элемента и генерирует быстрый специализированный код. Гибкость на уровне исходника, скорость на уровне машинного кода.
Свои параметрические типы
Структуру тоже можно сделать обобщённой по типу полей:
struct Pair2{T}
first::T
second::T
end
p_int = Pair2(1, 2)
p_str = Pair2("a", "b")
println(typeof(p_int))
println(typeof(p_str))Вывод:
Pair2{Int64}
Pair2{String}Здесь один шаблон Pair2{T} породил Pair2{Int64} и Pair2{String}. Поля имеют конкретный тип T, поэтому каждая версия компактна и быстра.
Ограничения параметров
Параметр можно ограничить подтипом: Vector{<:Number} — «вектор, элементы которого являются числами». В сигнатуре функции это пишут так:
total(v::Vector{T}) where {T <: Number} = sum(v)
println(total([1, 2, 3]))
println(total([1.5, 2.5]))Вывод:
6 4.0
Как работает под капотом
Это и есть технический ответ на вопрос «почему Julia быстрая». Когда параметр T известен (например, Vector{Int64}), компилятор знает, что каждый элемент занимает ровно 8 байт и лежит в памяти подряд. Он генерирует машинный код, работающий с этим типом без «упаковки» (boxing) и проверок типа в рантайме — как компилятор C для массива int. Сравните с Python-списком, где каждый элемент — отдельный объект с тегом типа, и интерпретатор проверяет тип на каждой операции.
Частые ошибки
Тонкость: Vector{Number} и Vector{<:Number} — разные вещи. Vector{Number} хранит элементы абстрактного типа (медленно, как «список чего угодно числового»), а Vector{<:Number} — это семейство конкретных типов вроде Vector{Int64}. Для скорости вам почти всегда нужен второй вариант. Не пишите контейнеры с абстрактным параметром в производительном коде.
Итоги
- Параметрический тип — шаблон с параметром:
Vector{T},Complex{T}. Vector{Int64}иVector{Float64}— разные конкретные типы из одного шаблона.- Свои структуры тоже делают обобщёнными:
struct Pair2{T}. - Параметр можно ограничить:
where {T <: Number}. - Знание конкретного
Tпозволяет компилятору сгенерировать быстрый код без проверок типа в рантайме.