Julia Language
Кортеж
Поиск…
Синтаксис
- а,
- a, b
- a, b = xs
- ()
- (А,)
- (a, b)
- (a, b ...)
- Tuple {T, U, V}
- NTuple {N, T}
- Tuple {T, U, Vararg {V}}
замечания
Кортежи имеют гораздо лучшую производительность во время выполнения, чем массивы, по двум причинам: их типы более точны, а их неизменность позволяет им выделяться в стеке вместо кучи. Тем не менее, этот более точный ввод печатает с более сложными накладными расходами и большими трудностями при достижении стабильности типа .
Введение в кортежи
Tuple
s являются неизменяемыми упорядоченными наборами произвольных отдельных объектов, одного или того же типа или разных типов . Как правило, кортежи строятся с использованием синтаксиса (x, y)
.
julia> tup = (1, 1.0, "Hello, World!")
(1,1.0,"Hello, World!")
Отдельные объекты кортежа можно получить с помощью синтаксиса индексирования:
julia> tup[1]
1
julia> tup[2]
1.0
julia> tup[3]
"Hello, World!"
Они реализуют итерируемый интерфейс и поэтому могут быть повторены при использовании for
циклов :
julia> for item in tup
println(item)
end
1
1.0
Hello, World!
Кортежи также поддерживают множество общих функций коллекций, таких как reverse
или length
:
julia> reverse(tup)
("Hello, World!",1.0,1)
julia> length(tup)
3
Кроме того, кортежи поддерживают множество операций коллекций более высокого порядка , включая any
, all
, map
или broadcast
:
julia> map(typeof, tup)
(Int64,Float64,String)
julia> all(x -> x < 2, (1, 2, 3))
false
julia> all(x -> x < 4, (1, 2, 3))
true
julia> any(x -> x < 2, (1, 2, 3))
true
Пустой кортеж можно построить с помощью ()
:
julia> ()
()
julia> isempty(ans)
true
Однако для построения кортежа одного элемента требуется конечная запятая. Это связано с тем, что скобки ( (
и )
) в противном случае рассматривались бы как операции группировки, а не для построения кортежа.
julia> (1)
1
julia> (1,)
(1,)
Для согласованности конечная запятая также допускается для кортежей с несколькими элементами.
julia> (1, 2, 3,)
(1,2,3)
Типы кортежей
typeof
кортеж является подтипом Tuple
:
julia> typeof((1, 2, 3))
Tuple{Int64,Int64,Int64}
julia> typeof((1.0, :x, (1, 2)))
Tuple{Float64,Symbol,Tuple{Int64,Int64}}
В отличие от других типов данных, типы Tuple
являются ковариантными . Другие типы данных в Юлии обычно являются инвариантными. Таким образом,
julia> Tuple{Int, Int} <: Tuple{Number, Number}
true
julia> Vector{Int} <: Vector{Number}
false
Это так, потому что везде принят Tuple{Number, Number}
, так же будет Tuple{Int, Int}
, так как он также имеет два элемента, оба из которых являются числами. Это не относится к Vector{Int}
и Vector{Number}
, так как функция, принимающая Vector{Number}
может захотеть сохранить плавающую точку (например, 1.0
) или комплексное число (например, 1+3im
) в таких вектор.
Ковариация типов кортежей означает, что Tuple{Number}
(опять же, в отличие от Vector{Number}
) фактически является абстрактным типом:
julia> isleaftype(Tuple{Number})
false
julia> isleaftype(Vector{Number})
true
Конкретные подтипы Tuple{Number}
включают в себя Tuple{Int}
, Tuple{Float64}
, Tuple{Rational{BigInt}}
и т. Д.
Типы Tuple
могут содержать завершающий Vararg
качестве последнего параметра для указания неопределенного количества объектов. Например, Tuple{Vararg{Int}}
является типом всех кортежей, содержащих любое число Int
s, возможно, ноль:
julia> isa((), Tuple{Vararg{Int}})
true
julia> isa((1,), Tuple{Vararg{Int}})
true
julia> isa((1,2,3,4,5), Tuple{Vararg{Int}})
true
julia> isa((1.0,), Tuple{Vararg{Int}})
false
тогда как Tuple{String, Vararg{Int}}
принимает кортежи, состоящие из строки , за которой следует любое число (возможно, ноль) Int
s.
julia> isa(("x", 1, 2), Tuple{String, Vararg{Int}})
true
julia> isa((1, 2), Tuple{String, Vararg{Int}})
false
В сочетании с совпадением это означает, что Tuple{Vararg{Any}}
описывает любой кортеж. Действительно, Tuple{Vararg{Any}}
- это еще один способ сказать Tuple
:
julia> Tuple{Vararg{Any}} == Tuple
true
Vararg
принимает второй параметр числового типа, указывающий, сколько раз должен быть точно его первый параметр типа. (По умолчанию, если этот параметр не задан, этот второй тип-тип является typevar, который может принимать любое значение, поэтому любое количество Int
s принимается в Vararg
.) Типы Tuple
заканчивающиеся на заданный Vararg
, автоматически будут расширены до запрошенное количество элементов:
julia> Tuple{String,Vararg{Int, 3}}
Tuple{String,Int64,Int64,Int64}
Обозначение существует для однородных кортежей с заданным Vararg
: NTuple{N, T}
. В этих обозначениях N
обозначает количество элементов в кортеже, а T
обозначает принятый тип. Например,
julia> NTuple{3, Int}
Tuple{Int64,Int64,Int64}
julia> NTuple{10, Int}
NTuple{10,Int64}
julia> ans.types
svec(Int64,Int64,Int64,Int64,Int64,Int64,Int64,Int64,Int64,Int64)
Обратите внимание, что NTuple
за пределами определенного размера показаны просто как NTuple{N, T}
вместо расширенной формы Tuple
, но они все те же:
julia> Tuple{Int,Int,Int,Int,Int,Int,Int,Int,Int,Int}
NTuple{10,Int64}
Отправка по типам кортежей
Поскольку списки параметров функции Julia сами являются кортежами, диспетчеризация различных типов кортежей часто проще выполнять самими параметрами метода, часто с либеральным использованием для оператора «splatting» ...
Например, рассмотрим реализацию reverse
для кортежей, начиная с Base
:
revargs() = ()
revargs(x, r...) = (revargs(r...)..., x)
reverse(t::Tuple) = revargs(t...)
Таким образом, реализация методов на кортежах сохраняет стабильность типов , что имеет решающее значение для производительности. Мы видим, что для этого подхода нет накладных расходов с использованием макроса @code_warntype
:
julia> @code_warntype reverse((1, 2, 3))
Variables:
#self#::Base.#reverse
t::Tuple{Int64,Int64,Int64}
Body:
begin
SSAValue(1) = (Core.getfield)(t::Tuple{Int64,Int64,Int64},2)::Int64
SSAValue(2) = (Core.getfield)(t::Tuple{Int64,Int64,Int64},3)::Int64
return (Core.tuple)(SSAValue(2),SSAValue(1),(Core.getfield)(t::Tuple{Int64,Int64,Int64},1)::Int64)::Tuple{Int64,Int64,Int64}
end::Tuple{Int64,Int64,Int64}
Хотя это довольно сложно прочитать, здесь здесь просто создается новый кортеж со значениями 3-го, 2-го и 1-го элементов исходного кортежа соответственно. На многих машинах это сводится к чрезвычайно эффективному LLVM-коду, который состоит из загрузок и магазинов.
julia> @code_llvm reverse((1, 2, 3))
define void @julia_reverse_71456([3 x i64]* noalias sret, [3 x i64]*) #0 {
top:
%2 = getelementptr inbounds [3 x i64], [3 x i64]* %1, i64 0, i64 1
%3 = getelementptr inbounds [3 x i64], [3 x i64]* %1, i64 0, i64 2
%4 = load i64, i64* %3, align 1
%5 = load i64, i64* %2, align 1
%6 = getelementptr inbounds [3 x i64], [3 x i64]* %1, i64 0, i64 0
%7 = load i64, i64* %6, align 1
%.sroa.0.0..sroa_idx = getelementptr inbounds [3 x i64], [3 x i64]* %0, i64 0, i64 0
store i64 %4, i64* %.sroa.0.0..sroa_idx, align 8
%.sroa.2.0..sroa_idx1 = getelementptr inbounds [3 x i64], [3 x i64]* %0, i64 0, i64 1
store i64 %5, i64* %.sroa.2.0..sroa_idx1, align 8
%.sroa.3.0..sroa_idx2 = getelementptr inbounds [3 x i64], [3 x i64]* %0, i64 0, i64 2
store i64 %7, i64* %.sroa.3.0..sroa_idx2, align 8
ret void
}
Множественные возвращаемые значения
Кортежи часто используются для множественных возвращаемых значений. Большая часть стандартной библиотеки, включая две функции итерируемого интерфейса ( next
и done
), возвращает кортежи, содержащие два связанных, но разных значения.
Скобки вокруг кортежей могут быть опущены в определенных ситуациях, что упрощает реализацию нескольких возвращаемых значений. Например, мы можем создать функцию, возвращающую как положительные, так и отрицательные квадратные корни действительного числа:
julia> pmsqrt(x::Real) = sqrt(x), -sqrt(x)
pmsqrt (generic function with 1 method)
julia> pmsqrt(4)
(2.0,-2.0)
Назначение Destructuring можно использовать для распаковки множественных возвращаемых значений. Чтобы сохранить квадратные корни в переменных a
и b
, достаточно написать:
julia> a, b = pmsqrt(9.0)
(3.0,-3.0)
julia> a
3.0
julia> b
-3.0
Другим примером этого являются функции divrem
и fldmod
, которые выполняют одно целое (усечение или перекрытие, соответственно) деления и операции divrem
в divrem
и то же время:
julia> q, r = divrem(10, 3)
(3,1)
julia> q
3
julia> r
1