Julia Language
Tuplas
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