Όποιος έχει χρησιμοποιήσει Python έχει σχεδόν σίγουρα χρησιμοποιήσει list comprehensions, τα οποία αποτελούν κεντρικό στοιχείο της σύνταξης της Python από τις πρώτες κιόλας εκδόσεις.
Κάτι τόσο βολικό βρίσκει σταδιακά τον δρόμο του και σε άλλες γλώσσες, ανάμεσά τους και η Julia.
Τα comprehensions είναι επιλογή και όχι αναγκαιότητα για τους προγραμματιστές της Julia, αφού συνήθως υπάρχουν εναλλακτικές (broadcasting, συναρτήσεις ανώτερης τάξης κ.λπ.).
Ωστόσο, ένα comprehension συχνά προσφέρει έναν απλό, ευανάγνωστο και αποδοτικό τρόπο για να κατασκευάσεις έναν πίνακα. Η χρήση του είναι τελικά θέμα προσωπικού γούστου, και του πώς νιώθεις για την Python έναντι των συναρτησιακών γλωσσών.
Η σύνταξη είναι ως επί το πλείστον ένα άμεσο αντίγραφο της Python, αλλά με επεκτάσεις για πίνακες περισσότερων διαστάσεων.
Πολύ pythonικό, μαζί με την προαιρετική ρήτρα if.
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
Το σχήμα της εξόδου εξαρτάται από τις λεπτομέρειες του comprehension: συνήθως είναι ίδιο με τη συλλογή εισόδου σε απλές περιπτώσεις, αλλά με μια ρήτρα if γίνεται ένα επίπεδο Vector.
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
Όπως στην Python, μπορούμε να έχουμε πολλαπλές ρήτρες for με διαφορετικές μεταβλητές, με ίδιες ή διαφορετικές συλλογές εισόδου.
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
Αυτό ισοδυναμεί με εμφωλευμένους βρόχους. Η έξοδος είναι μονοδιάστατη, ακόμη και με πίνακα ως είσοδο.
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...)
Η προηγούμενη ενότητα περιέγραψε πολλαπλές ρήτρες for που χωρίζονται μόνο με κενά.
Σε αυτή την ενότητα υπάρχει ένα μόνο for και οι μεταβλητές χωρίζονται με κόμμα.
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)
Κάθε μεταβλητή στο comprehension δημιουργεί μια νέα διάσταση στην έξοδο, με μία καταχώριση για κάθε πιθανό συνδυασμό των μεταβλητών.
Στο παραπάνω παράδειγμα, το x αυξάνεται προς τα κάτω στις γραμμές, το y αυξάνεται προς τα δεξιά στις στήλες.
Υπάρχουν και μεγαλύτερες διαστάσεις, με τις συνήθεις προειδοποιήσεις για την ευαναγνωσιμότητα της εξόδου.
Οι προηγούμενες ενότητες επικεντρώθηκαν στην έξοδο πίνακα, με το comprehension μέσα σε αγκύλες [ ... ].
Όταν οι αγκύλες αντικαθίστανται από παρενθέσεις ( ... ), συμβαίνει κάτι διαφορετικό: παίρνουμε μια generator expression: έναν iterator με οκνηρή αξιολόγηση, που μπορεί να παράγει την επόμενη τιμή όταν του ζητηθεί.
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
Όταν το αποτέλεσμα ενός comprehension χρησιμοποιείται αμέσως για περαιτέρω επεξεργασία, μια γεννήτρια μπορεί να είναι αποδοτική ως προς τη μνήμη, αποφεύγοντας την ανάγκη να αποθηκευτεί ένας μεγάλος ενδιάμεσος πίνακας.
Μια γεννήτρια που χρησιμοποιείται ως όρισμα συνάρτησης δεν χρειάζεται επιπλέον παρενθέσεις.
# 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
Η σύνταξη της γεννήτριας είναι ίδια με των comprehensions, με μόνη διαφορά τις αγκύλες που την περιβάλλουν.
Οι γεννήτριες μπορούν επίσης να φανούν βολικές σε κατασκευαστές λεξικών.
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
Δουλεύεις σε ένα ηλεκτρονικό κατάστημα μόδας. Σου έρχεται η ιδέα για μια λειτουργία της ιστοσελίδας όπου προτείνονται σύνολα στον χρήστη. Αυτό που έχει ενδιαφέρον είναι τα πιθανά σύνολα, οι τιμές τους και η εξάλειψη των ασύμβατων επιλογών.
Αν και μπορεί να υπάρχουν διαφορετικοί τρόποι να λύσεις τα παρακάτω προβλήματα, όλα μπορούν να λυθούν με μια comprehension.
Υλοποίησε τη συνάρτηση clothingitem() ώστε να δέχεται δύο Tuple, το ένα με categories και το άλλο με τις αντίστοιχες qualities.
Η συνάρτηση πρέπει να επιστρέφει ένα Dict με τα categories ως κλειδιά και τα qualities ως τιμές.
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"
Η συνάρτηση σου πρέπει να μπορεί να χειριστεί οποιονδήποτε αριθμό categories.
Υλοποίησε τη συνάρτηση get_combinations() ώστε να δέχεται ένα Tuple με πάνω ρούχα και ένα Tuple με κάτω ρούχα.
Η συνάρτηση πρέπει να επιστρέφει το cartesian product των πλειάδων ως δισδιάστατο πίνακα (δηλ. Matrix).
Κάθε στοιχείο είναι ένας συνδυασμός (top, bottom) με τη μορφή Tuple.
Τα πάνω ρούχα αλλάζουν καθώς κατεβαίνεις τις γραμμές, τα κάτω ρούχα αλλάζουν καθώς προχωράς στις στήλες.
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"))
Κάθε κομμάτι ρούχου έχει ένα πεδίο price που σχετίζεται με αυτό, οπότε θα ήταν χρήσιμο να μπορείς να συγκρίνεις τη συνολική τιμή των συνόλων.
Υλοποίησε τη συνάρτηση get_prices(), η οποία δέχεται έναν πίνακα με συνδυασμούς ρούχων.
Η συνάρτηση πρέπει να επιστρέφει έναν πίνακα με το ίδιο σχήμα, που να περιέχει τις τιμές των συνόλων στις αντίστοιχες θέσεις τους.
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
Κάθε κομμάτι ρούχου έχει ένα πεδίο base_color.
Χρησιμοποίησε αυτό το πεδίο για να αφαιρέσεις όλους τους συνδυασμούς όπου το πάνω και το κάτω ρούχο έχουν το ίδιο βασικό χρώμα.
Υλοποίησε τη συνάρτηση filter_clashing() ώστε να δέχεται έναν πίνακα με συνδυασμούς ρούχων.
Επίστρεψε έναν μονοδιάστατο πίνακα (δηλ. Vector) με τους συνδυασμούς που ταιριάζουν.
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"))
Γράψου στο Exercism για να μάθεις και να κατακτήσεις Julia με 35 έννοιες128 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.