Cualquiera que haya usado Python casi con toda seguridad ha usado listas por comprensión, que han sido un elemento central de la sintaxis de Python desde las primeras versiones.
Algo tan cómodo se abre camino poco a poco en otros lenguajes, incluido Julia.
Para quienes programan en Julia, las comprensiones son una opción y no una necesidad, ya que normalmente existen alternativas (broadcasting, funciones de orden superior, etc.).
Sin embargo, una comprensión suele ofrecer una forma sencilla, legible y eficiente de construir un array. Al final, usarlas es una cuestión de gusto personal, y de cómo te sientes respecto a Python frente a los lenguajes funcionales.
La sintaxis es en su mayor parte una copia directa de Python, pero con extensiones para arrays de más dimensiones.
Muy al estilo de Python, incluida la cláusula if opcional.
julia> [x^2 for x in 1:3]
3-element Vector{Int64}:
1
4
9
julia> [x^2 for x in 1:3 if isodd(x^2)]
2-element Vector{Int64}:
1
9
La forma de la salida depende de los detalles de la comprensión: normalmente la misma que la colección de entrada en los casos simples, pero aplanada a un Vector por una cláusula if.
julia> m
2×3 Matrix{Int64}:
1 2 3
4 5 6
julia> [x^2 for x in m]
2×3 Matrix{Int64}:
1 4 9
16 25 36
julia> [x^2 for x in m if isodd(x^2)]
3-element Vector{Int64}:
1
25
9
Igual que en Python, podemos tener varias cláusulas for con variables distintas, con colecciones de entrada iguales o diferentes.
julia> [x * y for x in 1:3 for y in 4:6]
9-element Vector{Int64}:
4
5
6
8
10
12
12
15
18
julia> [x * y for x in 1:3 for y in 4:6 if isodd(x * y)]
2-element Vector{Int64}:
5
15
Esto equivale a bucles anidados. La salida es unidimensional, incluso con una matriz de entrada.
julia> m
2×3 Matrix{Int64}:
1 2 3
4 5 6
julia> [x*y for x in m for y in m]
36-element Vector{Int64}:
1
4
2
5
(truncated output...)
En la sección anterior vimos varias cláusulas for separadas solo por espacios.
En esta sección hay un solo for y las variables van separadas por comas.
julia> [(x, y) for x in 1:3, y in 4:6]
3×3 Matrix{Tuple{Int64, Int64}}:
(1, 4) (1, 5) (1, 6)
(2, 4) (2, 5) (2, 6)
(3, 4) (3, 5) (3, 6)
Cada variable de la comprensión crea una nueva dimensión en la salida, con una entrada por cada combinación posible de las variables.
En el ejemplo anterior, x aumenta hacia abajo por las filas y y aumenta a lo ancho de las columnas.
Se pueden usar más dimensiones, con las advertencias habituales sobre la legibilidad de la salida.
Las secciones anteriores se han centrado en la salida de tipo array, con la comprensión colocada entre corchetes [ ... ].
Cuando los corchetes se sustituyen por paréntesis ( ... ), ocurre algo distinto: obtenemos una generator expression: un iterador evaluado de forma perezosa que puede producir el siguiente valor cuando se le pide.
julia> g = ((x, y) for x in 1:3, y in 4:6)
Base.Generator{Base.Iterators.ProductIterator{Tuple{UnitRange{Int64}, UnitRange{Int64}}}, var"#9#10"}(var"#9#10"(), Base.Iterators.ProductIterator{Tuple{UnitRange{Int64}, UnitRange{Int64}}}((1:3, 4:6)))
# Indexing fails with a generator, the entries don't exist yet
julia> g[1, 2]
ERROR: MethodError: no method matching getindex(::Base.Generator{Base.Iterators.ProductIterator{Tuple{UnitRange{…}, UnitRange{…}}}, var"#9#10"}, ::Int64, ::Int64)
# conversion to array
julia> collect(g)
3×3 Matrix{Tuple{Int64, Int64}}:
(1, 4) (1, 5) (1, 6)
(2, 4) (2, 5) (2, 6)
(3, 4) (3, 5) (3, 6)
# generators are mainly designed for iteration
julia> [i * j for (i, j) in g]
3×3 Matrix{Int64}:
4 5 6
8 10 12
12 15 18
Cuando el resultado de una comprensión se usa de inmediato para seguir procesando, un generador puede ser eficiente en memoria, ya que evita tener que almacenar un array intermedio grande.
Un generador usado como argumento de una función no necesita paréntesis adicionales.
# inefficient
julia> v = [x^2 for x in 1:1e6];
julia> sum(v)
3.333338333335e17
# better - use a generator
julia> sum(x^2 for x in 1:1e6)
3.3333383333312755e17
La sintaxis de los generadores es idéntica a la de las comprensiones, salvo por los corchetes que los rodean.
Los generadores también pueden resultar cómodos en los constructores de diccionarios.
julia> Dict(x => x^2 for x in 1:5)
Dict{Int64, Int64} with 5 entries:
5 => 25
4 => 16
2 => 4
3 => 9
1 => 1
Trabajas en una tienda de moda online. Se te ocurre la idea de una funcionalidad del sitio web en la que se sugieren conjuntos al usuario. Lo que interesa son los posibles conjuntos, sus precios y eliminar las opciones que no combinan.
Aunque puede haber distintas formas de resolver los siguientes problemas, todos se pueden resolver con una comprensión.
Implementa clothingitem() para que reciba dos Tuple, uno de categories y otro de sus respectivas qualities.
La función debe devolver un Dict con las categories como claves y las qualities como valores.
julia> categories = ("item_name", "price", "color", "base_color");
julia> qualities = ("Descriptive Name", 99.00, "Ochre Red", "red");
julia> clothingitem(categories, qualities)
Dict{String, Any} with 4 entries:
"price" => 99.0
"item_name" => "Descriptive Name"
"base_color" => "red"
"color" => "Ochre Red"
Tu función debe poder manejar cualquier número de categories.
Implementa get_combinations() para que reciba un Tuple de prendas superiores y un Tuple de prendas inferiores.
La función debe devolver el cartesian product de los tuples como un array bidimensional (es decir, una Matrix).
Cada entrada es una combinación (top, bottom) como un Tuple.
Las prendas superiores cambian a lo largo de las filas y las inferiores, a lo largo de las columnas.
julia> tops = (
Dict("item_name" => "Dress shirt"),
Dict("item_name" => "Casual shirt")
);
julia> bottoms = (
Dict("item_name" => "Jeans"),
Dict("item_name" => "Dress trousers")
);
julia> get_combinations(tops, bottoms)
2×2 Matrix{Tuple{Dict{String, String}, Dict{String, String}}}:
(Dict("item_name"=>"Dress shirt"), Dict("item_name"=>"Jeans")) (Dict("item_name"=>"Dress shirt"), Dict("item_name"=>"Dress trousers"))
(Dict("item_name"=>"Casual shirt"), Dict("item_name"=>"Jeans")) (Dict("item_name"=>"Casual shirt"), Dict("item_name"=>"Dress trousers"))
Cada prenda de ropa tiene un campo price asociado, por lo que podría resultar útil poder comparar el precio total de los conjuntos.
Implementa get_prices(), que recibe un array de combinaciones de prendas.
La función debe devolver un array con la misma forma, con los precios de los conjuntos en sus posiciones respectivas.
julia> tops = (
Dict("item_name" => "Dress shirt", "base_color" => "blue", "price" => 35),
Dict("item_name" => "Casual shirt", "base_color" => "black", "price" => 20)
);
julia> bottoms = (
Dict("item_name" => "Jeans", "base_color" => "blue", "price" => 30),
Dict("item_name" => "Dress trousers", "base_color" => "black", "price" => 75)
);
julia> combomatrix = get_combinations(tops, bottoms);
julia> get_prices(combomatrix)
2×2 Matrix{Int}::
65 110
50 95
julia> filteredmatrix = filter_clashing(combomatrix)
1-element Vector{Tuple{Dict{String, Any}, Dict{String, Any}}}:
(Dict("price" => 20, "item_name" => "Casual shirt", "base_color" => "black"), Dict("price" => 30, "item_name" => "Jeans", "base_color" => "blue"))
julia> get_prices(filteredmatrix)
1-element Vector{Int}:
50
Cada prenda de ropa tiene un campo base_color.
Usa este campo para eliminar todas las combinaciones en las que la prenda superior y la inferior tengan el mismo color base.
Implementa filter_clashing() para que reciba un array de combinaciones de prendas.
Devuelve un array unidimensional (es decir, un Vector) con las combinaciones válidas.
julia> tops = (
Dict("item_name" => "Dress shirt", "base_color" => "blue", "price" => 35),
Dict("item_name" => "Casual shirt", "base_color" => "black", "price" => 20)
);
julia> bottoms = (
Dict("item_name" => "Jeans", "base_color" => "blue", "price" => 30),
Dict("item_name" => "Dress trousers", "base_color" => "black", "price" => 75)
);
julia> combos = get_combinations(tops, bottoms);
julia> filter_clashing(combos)
1-element Vector{Tuple{Dict{String, Any}, Dict{String, Any}}}:
(Dict("price" => 20, "item_name" => "Casual shirt", "base_color" => "black"), Dict("price" => 30, "item_name" => "Jeans", "base_color" => "blue"))
Regístrate en Exercism para aprender y dominar Julia con 35 conceptos128 ejercicios y mentoría humana real, todo gratis.