Οι καταγραφές είναι δομές δεδομένων που ομαδοποιούν σχετικές πληροφορίες με ετικέτες. Μοιάζουν με τα αντικείμενα της JavaScript ή της Java, ή με τα structs της C ή της Rust, αλλά έχουν μερικές βασικές διαφορές.
Οι καταγραφές δημιουργούνται με άγκιστρα και τα στοιχεία τους χωρίζονται με κόμματα.
firefly =
{ name = "Firefly"
, creator = "Joss Whedon"
, episodes = 14
}
Ο τύπος μιας καταγραφής ορίζεται επίσης με παρόμοια σύνταξη, μόνο που τα σύμβολα ίσον = αντικαθίστανται από άνω και κάτω τελείες :.
firefly : { name : String, creator : String, episodes : Int }
firefly =
{ name = "Firefly"
, creator = "Joss Whedon"
, episodes = 14
}
Το να επαναλαμβάνεις αυτούς τους ορισμούς τύπων είναι κουραστικό, οπότε το να χρησιμοποιείς ένα type alias για να μοιράζεσαι τον ορισμό είναι συνηθισμένη πρακτική.
type alias TvSeries =
{ name : String
, creator : String
, episodes : Int
}
firefly : TvSeries
firefly =
{ name = "Firefly"
, creator = "Joss Whedon"
, episodes = 14
}
Το type alias παρέχει επίσης αυτόματα μια συνάρτηση κατασκευαστή για την καταγραφή. Αυτό προκαλεί καμιά φορά έκπληξη, αφού οι συναρτήσεις στην Elm πρέπει να ξεκινούν με πεζό χαρακτήρα, αλλά οι κατασκευαστές τύπων αποτελούν μια εξαίρεση.
firefly : TvSeries
firefly = TvSeries "Firefly" "Joss Whedon" 14
Ο βασικός τρόπος για να προσπελάσεις την τιμή ενός πεδίου ενός στιγμιοτύπου καταγραφής είναι να χρησιμοποιήσεις τη σημειογραφία με τελεία, όπως το firefly.creator.
Ο μεταγλωττιστής της Elm υποστηρίζει επίσης ειδικές συναρτήσεις προσπέλασης που ξεκινούν με τελεία ., όπως το .creator, οι οποίες λειτουργούν σε οποιαδήποτε καταγραφή έχει πεδίο με αυτό το όνομα.
Αυτή είναι η δεύτερη εξαίρεση στον κανόνα ότι οι συναρτήσεις πρέπει να ξεκινούν με πεζό χαρακτήρα.
Στην πραγματικότητα, οι συναρτήσεις που ορίζει ο χρήστης πρέπει να ξεκινούν με πεζό χαρακτήρα, αλλά τα δύο παραδείγματα που μόλις ανακαλύψαμε παράγονται από τον μεταγλωττιστή, όχι από τον προγραμματιστή.
firefly : TvSeries
firefly =
{ name = "Firefly"
, creator = "Joss Whedon"
, episodes = 14
}
firefly.name
--> "Firefly"
.creator firefly
--> "Joss Whedon"
Οι καταγραφές μπορούν επίσης να αποδομηθούν σε δεσμεύσεις χρησιμοποιώντας την αντιστοίχιση προτύπων καταγραφής. Η αποδόμηση λειτουργεί με οποιαδήποτε καταγραφή έχει πεδία με τα σχετικά ονόματα.
episodesCount : TvSeries -> Int
episodesCount { episodes } =
episodes
complicatedCopy : TvSeries -> TvSeries
complicatedCopy show =
let
{ name, creator } = show
in
{ name = name
, creator = creator
, episodes = episodesCount show
}
Οι καταγραφές είναι αμετάβλητες, όπως τα πάντα στην Elm, οπότε μόλις οριστεί μια καταγραφή, οι τιμές των πεδίων της δεν μπορούν ποτέ να αλλάξουν.
Υπάρχει μια ειδική σύνταξη για να δημιουργήσεις αντίγραφο μιας υπάρχουσας καταγραφής, αλλάζοντας ένα ή περισσότερα πεδία.
Αυτή η σύνταξη χρησιμοποιεί το σύμβολο της κάθετης γραμμής | για να ξεχωρίζει την αρχική καταγραφή από τα πεδία που θα αλλάξουν, όπως το { oldRecord | field1 = newValue }.
firefly : TvSeries
firefly =
{ name = "Firefly"
, creator = "Joss Whedon"
, episodes = 14
}
updatedFirefly : TvSeries
updatedFirefly =
{ firefly | creator = "J Whedon", episodes = 15 }
Η Elm χρησιμοποιεί [δομική ισότητα][equality], που σημαίνει ότι δύο στιγμιότυπα της ίδιας καταγραφής με πανομοιότυπες τιμές είναι ίσα.
firefly1 = TvSeries "Firefly" "Joss Whedon" 14
firefly2 = TvSeries "Firefly" "Joss Whedon" 14
firefly1 == firefly2
--> True
Η Elm υποστηρίζει επίσης δομική τυποποίηση, που σημαίνει ότι αν μια συνάρτηση απαιτεί μια καταγραφή με πεδία x και y, θα λειτουργήσει με οποιαδήποτε καταγραφή έχει αυτά τα πεδία, όπως σημεία 2D, σημεία 3D, διαστημόπλοια κ.λπ.
Αυτοί οι τύποι καταγραφών έχουν μια κάθετη γραμμή | στον ορισμό τους, όπως το { a | x : Float, y : Float }, και ονομάζονται "επεκτάσιμες καταγραφές" στην ορολογία της Elm.
Πρόσεχε τη διαφορά ανάμεσα σε μια κάθετη γραμμή | σε έναν ορισμό τύπου, που είναι ορισμός επεκτάσιμης καταγραφής ({ a | x : Int }), και σε μια κάθετη γραμμή | σε ένα πραγματικό στιγμιότυπο καταγραφής, που σημαίνει ότι ενημερώνουμε κάποια πεδία αυτής της καταγραφής.
point2d = { x = 1, y = 2 }
point3d = { x = 3, y = 4, z = 7 }
.x point2d --> 1
.x point3d --> 3
length : { a | x : Float, y : Float } -> Float
length vector =
sqrt (vector.x * vector.x + vector.y * vector.y)
length point2d --> 2.236068
length point3d --> 5
translateX : { a | x : Float } -> { a | x : Float }
translateX vector =
{ vector | x = vector.x + 1 }
translateX point2d --> { x = 2, y = 2 }
translateX point3d --> { x = 4, y = 4, z = 7 }
Σε αυτή την άσκηση είσαι μεγάλος φαν του αθλητισμού και μόλις ανακάλυψες το πάθος σου για το μπάσκετ του NBA. Καθώς είσαι νέος στο μπάσκετ του NBA, εμβαθύνεις στην ιστορία του NBA, παρακολουθείς ομάδες, προπονητές και τα στατιστικά νικών/ηττών τους, και τα συγκρίνεις μεταξύ τους.
Επειδή δεν έχεις ακόμα αγαπημένη ομάδα, θα αναπτύξεις και έναν αλγόριθμο για να καταλάβεις αν αξίζει να υποστηρίξεις μια συγκεκριμένη ομάδα. Έχεις επτά εργασίες που θα σε βοηθήσουν να αναπτύξεις τον δικό σου ιδιόκτητο αλγόριθμο υποστήριξης ομάδας.
Όρισε το ψευδώνυμο τύπου Coach για μια καταγραφή με τα εξής δύο πεδία, με την εξής σειρά:
name: το όνομα του προπονητή, τύπου String.formerPlayer: δηλώνει αν ο προπονητής ήταν παλιός παίκτης, τύπου Bool.Όρισε το ψευδώνυμο τύπου Stats για μια καταγραφή με τα εξής δύο πεδία, με την εξής σειρά:
wins: ο αριθμός των νικών, τύπου Int.losses: ο αριθμός των ηττών, τύπου Int.Όρισε το ψευδώνυμο τύπου Team για μια καταγραφή με τα εξής τρία πεδία, με την εξής σειρά:
name: το όνομα της ομάδας, τύπου String.coach: ο προπονητής της ομάδας, τύπου Coach.stats: τα στατιστικά της ομάδας, τύπου Stats.Θέλεις να συνεργαστείς με κώδικα τρίτων που έχει την έννοια της ομάδας, αλλά καλεί τον κώδικά σου με τις παραμέτρους σε διαφορετική σειρά. Πρόσθεσε μια συνάρτηση createTeam με την εξής υπογραφή String -> Stats -> Coach -> Team.
Καθώς οι ιδιοκτήτες των ομάδων του NBA είναι ανυπόμονοι, ανακάλυψες ότι τα άσχημα αποτελέσματα μιας ομάδας συχνά οδηγούσαν στην αντικατάσταση του προπονητή.
Υλοποίησε τη συνάρτηση replaceCoach που παίρνει ως παραμέτρους τον νέο προπονητή και την ομάδα, και επιστρέφει την ομάδα με τον νέο προπονητή:
let
coach = { name = "Larry Bird", formerPlayer = True }
stats = { wins = 58, losses = 24 }
team = { name = "Indiana Pacers", coach = coach, stats = stats }
newCoach = { name = "Isiah Thomas", formerPlayer = True }
in
replaceCoach newCoach team
--> { name = "Indiana Pacers"
-- , coach = { name = "Isiah Thomas", formerPlayer = True }
-- , stats = { wins = 58, losses = 24 } }
Στο μέλλον σχεδιάζεις να φτιάξεις έναν περίπλοκο αλγόριθμο για το αν αξίζει να υποστηρίξεις μια ομάδα ή όχι, αλλά για την πρώτη προσπάθεια αποφάσισες ότι πρέπει να υποστηρίξεις κάθε ομάδα που έχει περισσότερες νίκες παρά ήττες.
Θέλεις αυτή η συνάρτηση να μπορεί να χρησιμοποιηθεί ξανά σε άλλα πλαίσια, οπότε χρησιμοποίησε μια επεκτάσιμη καταγραφή στην υπογραφή τύπου για να δέχεσαι οποιαδήποτε καταγραφή έχει τα σχετικά πεδία.
Για να κρατήσεις τη συνάρτηση όσο πιο συνοπτική γίνεται, θέλεις να χρησιμοποιήσεις αντιστοίχιση προτύπων για να πάρεις το stats από την παράμετρο της συνάρτησης.
Υλοποίησε τη συνάρτηση rootForTeam που παίρνει μια ομάδα και επιστρέφει True αν πρέπει να υποστηρίξεις αυτή την ομάδα· αλλιώς, επιστρέφει False.
let
spursCoach = { name = "Gregg Popovich", formerPlayer = False }
spursStats = { wins = 56, losses = 26 }
spursTeam = { name = "San Antonio Spurs", coach = spursCoach, stats = spursStats }
in
rootForTeam spursTeam
--> True
Γράψου στο Exercism για να μάθεις και να κατακτήσεις Elm με 28 έννοιες110 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.