Buscar..


Sintaxis

  • una,
  • a, b
  • a, b = xs
  • ()
  • (una,)
  • (a, b)
  • (a, b ...)
  • Tupla {T, U, V}
  • NTuple {N, T}
  • Tupla {T, U, Vararg {V}}

Observaciones

Las tuplas tienen un rendimiento de tiempo de ejecución mucho mejor que las matrices por dos razones: sus tipos son más precisos y su inmutabilidad permite que se asignen en la pila en lugar de la pila. Sin embargo, esta tipificación más precisa viene con más sobrecarga de tiempo de compilación y más dificultad para lograr la estabilidad del tipo .

Introducción a las tuplas

Tuple son colecciones ordenadas inmutables de objetos distintos arbitrarios, ya sea del mismo tipo o de tipos diferentes. Normalmente, las tuplas se construyen utilizando la sintaxis (x, y) .

julia> tup = (1, 1.0, "Hello, World!")
(1,1.0,"Hello, World!")

Los objetos individuales de una tupla se pueden recuperar mediante la sintaxis de indexación:

julia> tup[1]
1

julia> tup[2]
1.0

julia> tup[3]
"Hello, World!"

Implementan la interfaz iterable y, por lo tanto, se pueden iterar sobre el uso for bucles :

julia> for item in tup
           println(item)
       end
1
1.0
Hello, World!

Las tuplas también admiten una variedad de funciones de colecciones genéricas, como reverse o length :

julia> reverse(tup)
("Hello, World!",1.0,1)

julia> length(tup)
3

Además, las tuplas admiten una variedad de operaciones de recopilación de orden superior , que incluyen any , all , map o 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

La tupla vacía se puede construir usando () :

julia> ()
()

julia> isempty(ans)
true

Sin embargo, para construir una tupla de un elemento, se requiere una coma al final. Esto se debe a que los paréntesis ( ( y ) ) se tratarían de otra manera como operaciones de agrupación en lugar de construir una tupla.

julia> (1)
1

julia> (1,)
(1,)

Por coherencia, también se permite una coma final para tuplas con más de un elemento.

julia> (1, 2, 3,)
(1,2,3)

Tipos de tuplas

El typeof una tupla es un subtipo de la Tuple :

julia> typeof((1, 2, 3))
Tuple{Int64,Int64,Int64}

julia> typeof((1.0, :x, (1, 2)))
Tuple{Float64,Symbol,Tuple{Int64,Int64}}

A diferencia de otros tipos de datos, los tipos de Tuple son covariantes . Otros tipos de datos en Julia son generalmente invariantes. Así,

julia> Tuple{Int, Int} <: Tuple{Number, Number}
true

julia> Vector{Int} <: Vector{Number}
false

Este es el caso porque en todas partes se acepta un Tuple{Number, Number} , también lo sería un Tuple{Int, Int} , ya que también tiene dos elementos, ambos de los cuales son números. Ese no es el caso de un Vector{Int} frente a un Vector{Number} , ya que una función que acepta un Vector{Number} puede desear almacenar un punto flotante (por ejemplo, 1.0 ) o un número complejo (por ejemplo, 1+3im ) en tales un vector.

La covarianza de los tipos de tuplas significa que Tuple{Number} (de nuevo, a diferencia de Vector{Number} ) es en realidad un tipo abstracto:

julia> isleaftype(Tuple{Number})
false

julia> isleaftype(Vector{Number})
true

Los subtipos concretos de Tuple{Number} incluyen Tuple{Int} , Tuple{Float64} , Tuple{Rational{BigInt}} , y así sucesivamente.

Tuple tipos de Vararg pueden contener un Vararg terminación como su último parámetro para indicar un número indefinido de objetos. Por ejemplo, Tuple{Vararg{Int}} es el tipo de todas las tuplas que contienen cualquier número de Int s, posiblemente cero:

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

mientras que Tuple{String, Vararg{Int}} acepta tuplas que consisten en una cadena , seguida de cualquier número (posiblemente cero) de Int s.

julia> isa(("x", 1, 2), Tuple{String, Vararg{Int}})
true

julia> isa((1, 2), Tuple{String, Vararg{Int}})
false

Combinado con la covarianza, esto significa que Tuple{Vararg{Any}} describe cualquier tupla. De hecho, Tuple{Vararg{Any}} es solo otra forma de decir Tuple :

julia> Tuple{Vararg{Any}} == Tuple
true

Vararg acepta un segundo parámetro de tipo numérico que indica cuántas veces exactamente debe ocurrir su primer parámetro de tipo. (Por defecto, si no se especifica, este segundo parámetro de tipo es un typevar que puede tomar cualquier valor, por lo que cualquier número de Int son aceptados s en el Vararg . Anteriores s) Tuple tipos que terminan en un especificado Vararg será automáticamente ampliado para la Número de elementos solicitados:

julia> Tuple{String,Vararg{Int, 3}}
Tuple{String,Int64,Int64,Int64}

Existe notación para tuplas homogéneas con un Vararg especificado: NTuple{N, T} . En esta notación, N denota el número de elementos en la tupla y T denota el tipo aceptado. Por ejemplo,

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)

Tenga en cuenta que los NTuple s más allá de cierto tamaño se muestran simplemente como NTuple{N, T} , en lugar de la forma de Tuple expandida, pero siguen siendo del mismo tipo:

julia> Tuple{Int,Int,Int,Int,Int,Int,Int,Int,Int,Int}
NTuple{10,Int64}

Despachando en tipos de tuplas

Debido a que las listas de parámetros de la función Julia son en sí mismas tuplas, el envío de varios tipos de tuplas a menudo es más fácil de realizar a través de los propios parámetros del método, a menudo con un uso liberal para el operador "salpicado" ... Por ejemplo, considere la implementación de reverse para tuplas, desde Base :

revargs() = ()
revargs(x, r...) = (revargs(r...)..., x)

reverse(t::Tuple) = revargs(t...)

La implementación de métodos en las tuplas de esta manera preserva la estabilidad del tipo , lo cual es crucial para el rendimiento. Podemos ver que no hay sobrecarga para este enfoque utilizando la macro @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}

Aunque es un poco difícil de leer, el código aquí consiste simplemente en crear una nueva tupla con los valores 3º, 2º y 1º de la tupla original, respectivamente. En muchas máquinas, esto se compila a un código LLVM extremadamente eficiente, que consiste en cargas y almacenes.

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
}

Múltiples valores de retorno

Las tuplas se utilizan con frecuencia para múltiples valores de retorno. Gran parte de la biblioteca estándar, incluidas dos de las funciones de la interfaz iterable ( next y done ), devuelve tuplas que contienen dos valores relacionados pero distintos.

Los paréntesis alrededor de las tuplas se pueden omitir en ciertas situaciones, lo que facilita la implementación de múltiples valores de retorno. Por ejemplo, podemos crear una función para devolver raíces cuadradas positivas y negativas de un número real:

julia> pmsqrt(x::Real) = sqrt(x), -sqrt(x)
pmsqrt (generic function with 1 method)

julia> pmsqrt(4)
(2.0,-2.0)

La asignación de destrucción se puede usar para descomprimir los múltiples valores de retorno. Para almacenar las raíces cuadradas en las variables a y b , basta con escribir:

julia> a, b = pmsqrt(9.0)
(3.0,-3.0)

julia> a
3.0

julia> b
-3.0

Otro ejemplo de esto son las funciones divrem y fldmod , que realizan una operación de división y resto de enteros (truncando o entablado, respectivamente) al mismo tiempo:

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
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow