Wer Python verwendet hat, hat mit ziemlicher Sicherheit schon List Comprehensions benutzt, die seit frühen Versionen fester Bestandteil der Python-Syntax sind.
Etwas so Praktisches hält nach und nach Einzug in andere Sprachen, auch in Julia.
Für Julia-Programmierer sind Comprehensions eher eine Option als eine Notwendigkeit, denn meist gibt es Alternativen (Broadcasting, Funktionen höherer Ordnung usw.).
Oft bietet eine Comprehension aber einen einfachen, gut lesbaren und performanten Weg, ein Array zu konstruieren. Ob du sie einsetzt, ist letztlich Geschmackssache, und hängt davon ab, wie du zu Python im Vergleich zu funktionalen Sprachen stehst.
Die Syntax ist größtenteils direkt aus Python übernommen, aber um Erweiterungen für höherdimensionale Arrays ergänzt.
Sehr pythonisch, inklusive der optionalen if-Klausel.
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
Die Form der Ausgabe hängt von den Details der Comprehension ab: in einfachen Fällen typischerweise dieselbe wie die der Eingabesammlung, durch eine if-Klausel aber zu einem Vector abgeflacht.
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
Wie in Python können wir mehrere for-Klauseln mit verschiedenen Variablen haben, wobei die Eingabesammlungen gleich oder verschieden sein können.
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
Das entspricht verschachtelten Schleifen. Die Ausgabe ist eindimensional, selbst wenn die Eingabe eine Matrix ist.
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...)
Im vorigen Abschnitt ging es um mehrere for-Klauseln, die nur durch Leerzeichen getrennt sind.
In diesem Abschnitt gibt es ein einziges for, und die Variablen werden durch Kommas getrennt.
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)
Jede Variable in der Comprehension erzeugt eine neue Dimension in der Ausgabe, mit einem Eintrag für jede mögliche Kombination der Variablen.
Im obigen Beispiel wächst x entlang der Zeilen nach unten, y entlang der Spalten nach rechts.
Höhere Dimensionen sind möglich, mit den üblichen Warnungen zur Lesbarkeit der Ausgabe.
In den vorigen Abschnitten ging es um die Ausgabe von Arrays, wobei die Comprehension in eckigen Klammern [ ... ] steht.
Wenn man die eckigen Klammern durch runde Klammern ( ... ) ersetzt, passiert etwas anderes: Wir erhalten einen generator expression, einen träge ausgewerteten Iterator, der den nächsten Wert auf Anfrage liefert.
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
Wenn das Ergebnis einer Comprehension sofort weiterverarbeitet wird, kann ein Generator speichereffizient sein und erspart es, ein großes Zwischenarray zu speichern.
Ein Generator, der als Funktionsargument verwendet wird, braucht keine zusätzlichen Klammern.
# 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
Die Syntax von Generatoren ist mit der von Comprehensions identisch, abgesehen von den umgebenden Klammern.
Generatoren sind auch in Konstruktoren für Wörterbücher praktisch.
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
Du arbeitest in einer Online-Modeboutique. Du hast die Idee für eine Website-Funktion, bei der dem Nutzer Outfits vorgeschlagen werden. Von Interesse sind die möglichen Outfits, ihre Preise und das Ausschließen nicht zusammenpassender Kombinationen.
Auch wenn es verschiedene Wege gibt, die folgenden Probleme zu lösen, lassen sich alle mit einer Comprehension lösen.
Implementiere clothingitem(), das zwei Tuples entgegennimmt, eines mit categories und das andere mit den zugehörigen qualities.
Die Funktion soll ein Dict zurückgeben, dessen Schlüssel die categories und dessen Werte die qualities sind.
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"
Deine Funktion soll mit beliebig vielen categories umgehen können.
Implementiere get_combinations(), das ein Tuple mit Oberteilen und ein Tuple mit Unterteilen entgegennimmt.
Die Funktion soll das cartesian product der Tuples als 2-D-Array (d. h. Matrix) zurückgeben.
Jeder Eintrag ist eine (top, bottom)-Kombination als Tuple.
Oberteile ändern sich von Zeile zu Zeile, Unterteile von Spalte zu Spalte.
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"))
Jedes Kleidungsstück hat ein zugehöriges price-Feld, daher kann es hilfreich sein, den Gesamtpreis von Outfits vergleichen zu können.
Implementiere get_prices(), das ein Array mit Kleidungskombinationen entgegennimmt.
Die Funktion soll ein Array mit derselben Form zurückgeben, in dem die Preise der Outfits an ihren jeweiligen Positionen stehen.
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
Jedes Kleidungsstück hat ein base_color-Feld.
Nutze dieses Feld, um alle Kombinationen zu entfernen, bei denen Oberteil und Unterteil dieselbe Grundfarbe haben.
Implementiere filter_clashing(), das ein Array mit Kleidungskombinationen entgegennimmt.
Gib ein 1-D-Array (d. h. Vector) mit den passenden Kombinationen zurück.
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"))
Melde dich bei Exercism an, um Julia mit 35 Konzepte128 Übungen und echtem menschlichen Mentoring zu lernen und zu meistern, alles kostenlos.