Μέχρι τώρα, το syllabus δεν έχει πει πολλά για τους τύπους, αλλά είναι σαφές ότι υπάρχουν στη Julia:
julia> vals = (42, 4.3, π, "hello", 'Q')
(42, 4.3, π, "hello", 'Q')
julia> typeof(vals)
Tuple{Int64, Float64, Irrational{:π}, String, Char}
Δεν προσδιορίσαμε ποτέ τους τύπους, αλλά η Julia τους ανέθεσε ούτως ή άλλως.
types, οι οποίοι είναι κεντρικοί στον σχεδιασμό της.Type Inference.Ο μεταγλωττιστής JIT θα κοιτάξει μέσα σε (όλο) τον κώδικα, θα δει πώς χρησιμοποιείται μια μεταβλητή, και θα συμπεράνει έναν κατάλληλο προεπιλεγμένο τύπο συμβατό με αυτή τη χρήση.
Το να βασίζεσαι στην εξαγωγή τύπων είναι εντάξει για την επίλυση απλών ασκήσεων εκμάθησης, αλλά για μεγαλύτερα προγράμματα πιθανότατα θα χρειαστείς πιο ακριβή έλεγχο.
Στους περισσότερους σύγχρονους επεξεργαστές, ένας ακέραιος έχει από προεπιλογή τον τύπο Int64.
Είδαμε στην Έννοια Numbers ότι μια τιμή μπορεί να μετατραπεί σε έναν συγκεκριμένο μη προεπιλεγμένο τύπο.
julia> x = Int16(42)
42
julia> typeof(x)
Int16
Ωστόσο, η μεταβλητή x μπορεί ακόμα να εκχωρηθεί ξανά σε διαφορετικό τύπο:
julia> x = "changed"
"changed"
julia> typeof(x)
String
Αυτό είναι type instability, το οποίο είναι:
Αντ' αυτού, μπορούμε να ορίσουμε τον τύπο του x χρησιμοποιώντας τον τελεστή :::
julia> y::Int16 = 42
42
julia> typeof(y)
Int16
julia> y = "changed"
ERROR: MethodError: Cannot `convert` an object of type String to an object of type Int16
The function `convert` exists, but no method is defined for this combination of argument types.
Τώρα το y είναι, και θα είναι πάντα, τύπου Int16.
Έτσι, ο μεταγλωττιστής ξέρει πόσα byte να δεσμεύσει για αυτό, και μπορεί να βελτιστοποιήσει τον υπόλοιπο κώδικα ώστε να βασίζεται σε έναν σταθερό τύπο.
Ως προς αυτό, η μεταβλητή είναι λίγο-πολύ παρόμοια με εκείνες σε μια στατικά τυποποιημένη γλώσσα όπως η C.
Int64, Int16, String, Char: από πού "προέρχονται" αυτοί οι τύποι;
Σε πολλές αντικειμενοστρεφείς (OO) γλώσσες, κάθε τύπος είναι μια κλάση, η δημιουργία υποκλάσεων τα τακτοποιεί σε μια ιεραρχία κλάσεων, και οι μέθοδοι κλάσης ορίζουν τις συμπεριφορές.
Η Java και η Ruby είναι προφανή παραδείγματα αυτού του μοτίβου, αλλά ακόμα και η Python είναι παρόμοια εσωτερικά.
Η Julia δεν έχει κλάσεις.
Ο τεκμηριωμένος λόγος είναι ότι τα χαρακτηριστικά του αντικειμενοστρεφούς προγραμματισμού παρεμβαίνουν στον μεταγλωττιστή JIT και βλάπτουν την απόδοση κατά την εκτέλεση.
Κι όμως, κοίτα αυτόν τον κώδικα:
julia> y::Int16 = 42
42
julia> typeof(y)
Int16
julia> supertype(Int16)
Signed
julia> supertypes(Int16)
(Int16, Signed, Integer, Real, Number, Any)
Συμπληρώνοντας κάποιες λεπτομέρειες:
Int16 είναι ένας τύπος, και μπορούμε να δημιουργήσουμε μεταβλητές αυτού του τύπου.Int16 είναι υπότυπος του Signed, και η συνάρτηση supertype() μας το δείχνει.Integer, Real και Number μέχρι το Any στην κορυφή, και η subtypes() θα μας απαριθμήσει αυτόν τον κλάδο της ιεραρχίας.Όλοι οι κλάδοι καταλήγουν στο Any, το οποίο έχει τη μοναδικότητα να είναι ο ίδιος ο υπέρτυπός του.
julia> supertypes(String)
(String, AbstractString, Any)
julia> supertype(Any)
Any
Έτσι, η Julia δεν έχει ιεραρχία class, αλλά έχει ιεραρχία type.
Κάποια στιγμή θα προσπαθήσουμε να ξεμπερδέψουμε πώς λειτουργεί αυτό, αλλά υπάρχουν πολλά ακόμα να εξερευνήσουμε πρώτα.
Μπορούμε να χρησιμοποιήσουμε τη typeof() για να ελέγξουμε την ισότητα με τον συνηθισμένο τρόπο.
julia> typeof(11)
Int64
julia> typeof(11) == Int64
true
julia> typeof(11) == Number
false
Η ισότητα τύπων πρέπει να είναι ακριβής, καθώς αυτή η μορφή σύγκρισης δεν έχει καμία αντίληψη της ιεραρχίας τύπων.
Πιο ευέλικτα, η isa θα μας πει αν μια τιμή έχει είτε τον ίδιο τύπο με το μέρος σύγκρισης, είτε κάποιον υπότυπό του.
Μπορεί να χρησιμοποιηθεί είτε σε μορφή συνάρτησης είτε σε μορφή ένθετου τελεστή.
julia> 12 isa Int64
true
julia> 12 isa Number
true
julia> isa(12, Number)
true
julia> 12 isa String
false
Σημείωσε ότι η isa περιμένει ένα value στα αριστερά, όχι ένα type.
Το να προσπαθήσεις να συγκρίνεις δύο τύπους με αυτόν τον τρόπο θα δώσει απροσδόκητα αποτελέσματα.
Ο σωστός τελεστής είναι το <:, το οποίο θα δούμε πολύ περισσότερο σε μελλοντικές έννοιες.
julia> Int64 isa Number ## Don't do this!
false
julia> Int64 <: Number
true
Είδαμε ότι η ιεραρχία τύπων σχηματίζει μια δενδρική δομή (με την έννοια της επιστήμης των υπολογιστών, με τη ρίζα στην κορυφή).
Κάθε στοιχείο του δέντρου είναι ένα node, και αυτά μπορούν να χωριστούν σε κατηγορίες:
abstract.concrete.julia> subtypes(Integer) # an abstract type
3-element Vector{Any}:
Bool
Signed
Unsigned
julia> subtypes(Int64) # a concrete type
Type[]
Αυτή είναι μια σημαντική διάκριση, γιατί μόνο οι συγκεκριμένοι τύποι μπορούν να γίνουν instantiated ως μεταβλητές.
julia> a::Int16 = 42
42
julia> typeof(a)
Int16
julia> b::Integer = 42
42
julia> typeof(b)
Int64
Σημείωσε ότι το να προσπαθήσεις να χρησιμοποιήσεις έναν αφηρημένο τύπο δεν εμφανίζει κανένα μήνυμα σφάλματος (σε αυτή την περίπτωση), αλλά ο μεταγλωττιστής δημιουργεί έναν κατάλληλο συγκεκριμένο τύπο: Int64 αντί για Integer.
Σε αυτή την άσκηση, θα ασχοληθείς λίγο με τη μηχανική δεδομένων, κάνοντας προεπεξεργασία σε κάποια δεδομένα βαθμολόγησης. Ένα σχολείο με πάρα, πάρα, πάρα πολλούς (μήπως πάρα πολλούς;) μαθητές προσπαθεί να κάνει κάποια ανάλυση δεδομένων στους βαθμούς που πήραν οι μαθητές και θέλει να το κάνει αποδοτικά, οπότε η ιδέα είναι να ελαχιστοποιήσει και να οργανώσει τα δεδομένα.
Η κλίμακα βαθμολόγησης είναι συνήθως από το 0 έως το 10, οπότε είναι πιο αποδοτικό να χρησιμοποιείς έναν τύπο δεδομένων με μόνο οκτώ bits ανά βαθμό.
UInt8.Int8.Αφού υλοποιήσεις τη συνάρτηση για τη μετατροπή των βαθμών, θα χρειαστεί να γράψεις άλλη μία που να χειρίζεται τις συλλογές στις οποίες είναι αποθηκευμένοι, υποβιβάζοντας τους βαθμούς και επιστρέφοντάς τους σε φθίνουσα σειρά.
Vectors ταξινομημένα σε αύξουσα σειρά για να αποθηκεύουν τους βαθμούς τους, γιατί είναι σχολαστικοί με τα δεδομένα τους.Sets, τα οποία δεν είναι ταξινομημένα, για να αποθηκεύουν τους βαθμούς τους, γιατί είναι λίγο πιο τεμπέληδες.Και στις δύο συναρτήσεις, θα χρειαστεί να χειριστείς μη έγκυρες εισόδους πετώντας ένα MethodError.
Ο χειρισμός εξαιρέσεων θα καλυφθεί σε επόμενη Έννοια, οπότε για τις ανάγκες αυτής της άσκησης, μπορείς να χρησιμοποιήσεις την παρακάτω σύνταξη:
throw(MethodError(f, args))
Όπου f είναι η συνάρτηση και args είναι μια πλειάδα με τα ορίσματα που δίνονται στη συνάρτηση.
Με τη συνάρτηση demote(n), το f είναι demote και το args είναι (n,):
throw(MethodError(demote, (n,)))
Υλοποίησε τη μέθοδο demote(n).
Float64, θα πρέπει να στρογγυλοποιεί προς τα πάνω στον πλησιέστερο ακέραιο και να επιστρέφει έναν τύπο δεδομένων UInt8.Integer, θα πρέπει να επιστρέφει τον ίδιο ακέραιο σε τύπο δεδομένων Int8.julia> demote(4.2)::UInt8
5
julia> demote(4)::Int8
4
julia> demote("hi")
MethodError: no method matching demote(::String) #output truncated
Υλοποίησε τη μέθοδο preprocess(coll).
Vector, θα πρέπει να υποβιβάζει όλους τους αριθμούς και να αντιστρέφει το διάνυσμα.Set, θα πρέπει να υποβιβάζει όλους τους αριθμούς και να επιστρέφει ένα ταξινομημένο διάνυσμα σε φθίνουσα σειρά.julia> preprocess([1, 2, 3])
3-element Vector{Int8}:
3
2
1
julia> preprocess(Set([2.2, 5.8, 3.4]))
3-element Vector{UInt8}:
6
4
3
julia> preprocess(42)
MethodError: no method matching preprocess(::Int64) #output truncated
Γράψου στο Exercism για να μάθεις και να κατακτήσεις Julia με 35 έννοιες128 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.