Поиск…


Синтаксис

  • а,
  • 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


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow