Ένας κεντρικός στόχος της Julia είναι να μπορεί να εκτελεί υπολογισμούς σε αριθμητικά δεδομένα, γρήγορα και αποδοτικά. Πάρα πολλά αριθμητικά δεδομένα.
Συνήθως, τα δεδομένα αποθηκεύονται σε πίνακες (ή σε κάποιον συναφή τύπο), οπότε είναι εύλογο να πούμε ότι οι πίνακες βρίσκονται στην καρδιά της γλώσσας Julia.
Οι πίνακες μπορούν να έχουν οποιοδήποτε μέγεθος (με μόνο περιορισμό τη μνήμη του υλικού σου) και μπορούν να έχουν οσοδήποτε πολλές διαστάσεις.
Κάποιοι τύποι πινάκων έχουν ειδικά ονόματα (δανεισμένα από τη Γραμμική Άλγεβρα).
Ένας πίνακας 1 διάστασης ονομάζεται Vector και ένας πίνακας 2 διαστάσεων ονομάζεται Matrix.
Και οι δύο είναι υποτύποι του Array στη Julia.
Αυτή η εισαγωγική έννοια επικεντρώνεται στα βασικά των Vector. Οι πίνακες περισσότερων διαστάσεων θα καλυφθούν αργότερα στην ύλη.
Στη Julia, ένα Vector είναι μια διατεταγμένη ακολουθία τιμών στις οποίες μπορείς να προσπελάσεις με αριθμό θέσης.
Ο πιο απλός τρόπος να δημιουργήσεις ένα Vector είναι να απαριθμήσεις τις τιμές μέσα σε αγκύλες:
julia> num_vec = [1, 4, 9]
3-element Vector{Int64}:
1
4
9
julia> str_vec = ["arrays", "are", "important"]
3-element Vector{String}:
"arrays"
"are"
"important"
Ο τύπος του Vector αντιστοιχεί στον τύπο κάθε στοιχείου του.
Τεχνικά, είναι Vector στήλης, αλλά για την ώρα αγνόησέ το, αν δεν είσαι εξοικειωμένος με τη Γραμμική Άλγεβρα. Θα βγάλει περισσότερο νόημα μετά την έννοια των Πολυδιάστατων Πινάκων.
Άρα τα Vector πρέπει να είναι ομοιογενή (όλα τα στοιχεία ίδιου τύπου);
Σε κάποιο βαθμό, ναι, και μάλιστα συνιστάται να το επιδιώκεις αυτό αν θέλεις την καλύτερη απόδοση.
Ωστόσο, η Julia μπορεί να παρακάμψει αυτόν τον περιορισμό (όταν χρειάζεται) χρησιμοποιώντας τον τύπο Any:
julia> mixed_vector = [1, "str", 'c']
3-element Vector{Any}:
1
"str"
'c': ASCII/Unicode U+0063 (category Ll: Letter, lowercase)
Όταν δημιουργείς ένα κενό Vector, η Julia προεπιλέγει τον τύπο στοιχείου Any, αλλά μπορείς να το προσδιορίσεις πιο συγκεκριμένα δηλώνοντας τον επιθυμητό τύπο στοιχείου στον κενό πίνακα.
julia> empty_vec = []
Any[]
julia> empty_int_vec = Int64[]
Int64[]
Είναι πολύ συνηθισμένο να ξεκινάς από Vector γεμάτα με 0 ή με 1. Γι' αυτά υπάρχουν οι συναρτήσεις που ονομάζονται (όχι και τόσο αναπάντεχα) zeros() και ones(), οι οποίες παίρνουν ως παράμετρο το μέγεθος του Vector.
Ο προεπιλεγμένος τύπος είναι Float64, αλλά μπορούν προαιρετικά να οριστούν και άλλοι αριθμητικοί τύποι.
Κατ' επέκταση, η συνάρτηση fill() παίρνει και μια τιμή προς επανάληψη και το επιθυμητό μέγεθος του Vector.
julia> zeros(3)
3-element Vector{Float64}:
0.0
0.0
0.0
julia> ones(3)
3-element Vector{Float64}:
1.0
1.0
1.0
julia> ones(Int64, 3)
3-element Vector{Int64}:
1
1
1
julia> fill(42, 3)
3-element Vector{Int64}:
42
42
42
Υπάρχουν πολλές άλλες επιλογές: δες το εγχειρίδιο.
Διάβασε και θυμήσου αυτόν τον κανόνα:
Αυτό είναι γνώριμο σε όποιον έχει χρησιμοποιήσει άλλες γλώσσες επιστήμης δεδομένων (R, Matlab, Fortran...), αλλά μπορεί να φανεί σοκαριστικό σε επιστήμονες υπολογιστών με υπόβαθρο σε γλώσσες σαν τη C.
Κατά τα άλλα, οι θέσεις δουλεύουν όπως ίσως μαντεύεις: απλώς βάλ' τες μέσα σε αγκύλες:
squares = [0, 1, 4, 9, 16]
squares[1] # 0
squares[3] # 4
squares[begin] # 0 ("begin" is synonym for 1)
squares[end] # 16 ("end" is synonym for length(squares))
Πρόσεξε την ευκολία της προσπέλασης με το end (που είναι πολύ χρήσιμο) και με το begin (που μάλλον δεν είναι).
Όσοι προγραμματίζουν σε Python ίσως αναρωτιούνται για τις αρνητικές θέσεις.
Μην το κάνεις: δεν υπάρχουν στη Julia και θα προκαλέσουν BoundsError, όπως και κάθε θέση μικρότερη από 1 ή μεγαλύτερη από length(squares).
Τα Vector στη Julia είναι μεταβλητά: μπορούμε να αλλάξουμε το περιεχόμενο μεμονωμένων κελιών.
julia> vals = [1, 3, 5, 7]
4-element Vector{Int64}:
1
3
5
7
julia> vals[2] = 4 # reassign the value of this element
4
# Only the value at position 2 changes:
julia> vals
4-element Vector{Int64}:
1
4
5
7
Υπάρχουν πολλές διαθέσιμες συναρτήσεις για τον χειρισμό Vector, αν και η τεκμηρίωση της Julia τις γενικεύει σε "συλλογές" και όχι σε Vector.
Σημείωσε ότι, κατά σύμβαση, οι συναρτήσεις που μεταβάλλουν το όρισμά τους έχουν ! στο όνομά τους.
Ο μεταγλωττιστής δεν το επιβάλλει, αλλά είναι μια πολύ ισχυρή σύμβαση στον κόσμο της Julia.
Αυτές είναι μερικές από τις χρήσιμες συναρτήσεις:
push!().pop!().pushfirst!() και popfirst!().insert!() και deleteat!().julia> vals = [1, 3]
2-element Vector{Int64}:
1
3
julia> push!(vals, 5, 6) # can add multiple values
4-element Vector{Int64}:
1
3
5
6
julia> pop!(vals) # mutates vals, return popped value
6
julia> vals
3-element Vector{Int64}:
1
3
5
Η συνένωση είναι αρκετά απλή για τα Vector, αν και μια ματιά στο εγχειρίδιο προειδοποιεί για πολυπλοκότητες που μας περιμένουν όταν φτάσουμε στους πολυδιάστατους πίνακες.
Η συνάρτηση append!() μπορεί να πάρει ως είσοδο έναν συνδυασμό από πολλά Vector και μεμονωμένα στοιχεία.
julia> append!([1, 2], [3, 4], [-1, -2], 15)
7-element Vector{Int64}:
1
2
3
4
-1
-2
15