Quem já usou Python quase certamente já usou compreensões de listas, que são parte central da sintaxe do Python desde as primeiras versões.
Algo tão conveniente acaba, aos poucos, chegando a outras linguagens, inclusive Julia.
Para quem programa em Julia, as compreensões são uma opção, não uma necessidade, já que geralmente existem alternativas (broadcasting, funções de ordem superior etc.).
No entanto, uma compreensão muitas vezes oferece uma forma simples, legível e eficiente de construir um array. No fim, usá-las é uma questão de gosto pessoal, e de como você se sente em relação a Python versus linguagens funcionais.
A sintaxe é, em grande parte, uma cópia direta do Python, mas com extensões para arrays de dimensões maiores.
Bem ao estilo Python, incluindo a 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
O formato da saída depende dos detalhes da compreensão: normalmente é igual ao da coleção de entrada em casos simples, mas é achatado para um Vector por uma 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
Assim como em Python, podemos ter várias cláusulas for com variáveis diferentes, usando a mesma coleção de entrada ou coleções 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
Isso equivale a laços aninhados. A saída é unidimensional, mesmo com uma matriz na 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...)
A seção anterior mostrou várias cláusulas for separadas apenas por espaços.
Nesta seção, há um único for e as variáveis são separadas por vírgulas.
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 variável da compreensão cria uma nova dimensão na saída, com uma entrada para cada combinação possível das variáveis.
No exemplo acima, x aumenta descendo pelas linhas e y aumenta percorrendo as colunas.
Dimensões maiores são possíveis, com os avisos de sempre sobre a legibilidade da saída.
As seções anteriores focaram na saída em array, com a compreensão colocada entre colchetes [ ... ].
Quando os colchetes são substituídos por parênteses ( ... ), acontece algo diferente: obtemos uma generator expression, um iterador de avaliação preguiçosa que pode fornecer o próximo valor sob demanda.
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
Quando o resultado de uma compreensão é usado imediatamente em processamento posterior, um gerador pode economizar memória, evitando a necessidade de armazenar um array intermediário grande.
Um gerador usado como argumento de função não precisa de parênteses adicionais.
# 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
A sintaxe do gerador é idêntica à das compreensões, a não ser pelos delimitadores que os envolvem.
Geradores também podem ser úteis em construtores de dicionário.
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
Você trabalha em uma loja de moda online. Você tem a ideia de criar um recurso no site em que looks são sugeridos ao usuário. Interessam os looks possíveis, seus preços e a eliminação das opções que não combinam.
Embora possa haver diferentes formas de resolver os problemas a seguir, todos podem ser resolvidos com uma compreensão de lista.
Implemente clothingitem() para receber dois Tuples, um de categories e outro das respectivas qualities.
A função deve retornar um Dict com as categories como chaves e as 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"
Sua função deve ser capaz de lidar com qualquer número de categories.
Implemente get_combinations() para receber um Tuple de peças de cima e um Tuple de peças de baixo.
A função deve retornar o cartesian product dos tuples como um array 2-D (ou seja, uma Matrix).
Cada entrada é uma combinação (top, bottom) como um Tuple.
As peças de cima variam ao longo das linhas, as peças de baixo variam ao longo das colunas.
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 peça de roupa tem um campo price associado a ela, então pode ser útil conseguir comparar o preço total dos looks.
Implemente get_prices(), que recebe um array de combinações de roupas.
A função deve retornar um array com formato semelhante, com os preços dos looks em suas respectivas posições.
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 peça de roupa tem um campo base_color.
Use esse campo para remover todas as combinações em que a peça de cima e a peça de baixo têm a mesma cor base.
Implemente filter_clashing() para receber um array de combinações de roupas.
Retorne um array 1-D (ou seja, um Vector) das combinações resultantes.
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"))
Crie sua conta no Exercism para aprender e dominar Julia com 35 conceitos128 exercícios e mentoria humana de verdade, tudo de graça.