Διαδρομές
/
Julia
Julia
/
Ασκήσεις
/
Αισθητήρες εργοστασίου
Αισθητήρες εργοστασίου

Αισθητήρες εργοστασίου

Άσκηση εκμάθησης

Εισαγωγή

Οι προγραμματιστές συνήθως προσπαθούν να γράψουν τέλειο λογισμικό, και συνήθως αποτυγχάνουν.

Τα πράγματα πηγαίνουν στραβά, απροσδόκητα, και πρέπει να μπορούμε να τα αντιμετωπίσουμε.

Κάποιοι σχεδιαστές γλωσσών πιστεύουν ότι προτεραιότητα είναι να εντοπιστεί ένα σφάλμα όσο το δυνατόν γρηγορότερα και μετά να τερματιστεί η εκτέλεση με ένα ενημερωτικό μήνυμα που βοηθά στην αποσφαλμάτωση.

Οι γλώσσες για την επιστήμη των δεδομένων έχουν την τάση να ακολουθούν μια πιο διαφοροποιημένη προσέγγιση. Κάποια σφάλματα είναι τόσο σοβαρά που ο άμεσος τερματισμός είναι απαραίτητος, αλλά συχνά είναι καλύτερο να επισημάνεις ένα πρόβλημα ως κάτι που θα αντιμετωπιστεί αργότερα και μετά να συνεχίσεις την εκτέλεση.

Είδαμε στην Έννοια Nothingness ότι η Julia παρέχει διάφορα σύμβολα θέσης για προβληματικές τιμές: missing, NaN και Inf. Το αν αυτά αποτελούν καλύτερη προσέγγιση από τον τερματισμό του προγράμματος σε μια συγκεκριμένη περίπτωση είναι θέμα κρίσης του προγραμματιστή.

Μια διευκρίνιση ορολογίας πριν μπούμε στις λεπτομέρειες: η τεκμηρίωση της Julia θεωρεί τις λέξεις "error" και "exception" σε μεγάλο βαθμό εναλλάξιμες. Το παρακάτω κείμενο μπορεί να είναι εξίσου ασυνεπές.

Τυπικοί τύποι σφαλμάτων

Σε αυτό το σημείο της ύλης, σίγουρα έχεις δει πολλά μηνύματα σφάλματος από τη Julia. Για παράδειγμα:

julia> Int(3.14)
ERROR: InexactError: Int64(3.14)

Η προσπάθεια μετατροπής ενός αριθμού κινητής υποδιαστολής σε ακέραιο συνεπάγεται απώλεια ακρίβειας, οπότε παίρνουμε ένα InexactError.

Το InexactError είναι ένας τύπος, ένας από τους αρκετούς (προς το παρόν 25) που είναι ενσωματωμένοι στη Julia ως στάνταρ. Όλοι τους είναι υποτύποι του Exception:

julia> supertype(InexactError)
Exception

throw()

Κάποιοι από τους τυπικούς τύπους σφαλμάτων μπορεί να σου φανούν χρήσιμοι για να τους παράγεις στον δικό σου κώδικα.

Όπως όλοι οι συγκεκριμένοι τύποι, έτσι και τα σφάλματα έχουν κατασκευαστές. Δέχονται διάφορα ορίσματα, οπότε δες την τεκμηρίωση για αυτόν που θέλεις να χρησιμοποιήσεις.

julia> DomainError(42, "out of range")
DomainError(42, "out of range")

Για να χρησιμοποιήσεις το σφάλμα, τύλιξε τον κατασκευαστή σε μια συνάρτηση throw():

julia> throw(DomainError(42, "out of range"))
ERROR: DomainError with 42:
out of range

error()

Για μια γρήγορη και πρόχειρη λύση, η συνάρτηση error() μπορεί να είναι βολική. Παίρνει ως όρισμα μια συμβολοσειρά (ή τα συστατικά μιας συμβολοσειράς):

julia> happy = false;
julia> happy || error("😞 something went wrong")
ERROR: 😞 something went wrong

Προσαρμοσμένα σφάλματα

Η δημιουργία νέων τύπων σφαλμάτων είναι καταρχήν πολύ εύκολη. Απλώς πρόσθεσε έναν ακόμη υποτύπο του Exception:

julia> struct MyError <: Exception end

julia> throw(MyError)
ERROR: MyError

Ισχυρισμοί

Η βασική ιδέα ενός ισχυρισμού είναι "αυτή η πρόταση οφείλει να είναι αληθής, οπότε διαμαρτυρήσου έντονα αν είναι ψευδής". Η αξία του φαίνεται κυρίως κατά την αποσφαλμάτωση, καθώς ο κώδικας παραγωγής δεν θα έπρεπε ποτέ να αποτυγχάνει σε έναν ισχυρισμό.

Είδαμε στην Έννοια Types ότι μπορούμε να προσθέσουμε ισχυρισμούς τύπων, για παράδειγμα για να ελέγξουμε τον τύπο επιστροφής μιας συνάρτησης.

julia> 42::Number
42

julia> "two"::Number
ERROR: TypeError: in typeassert, expected Number, got a value of type String

Γενικότερα, η μακροεντολή @assert μας επιτρέπει να ελέγξουμε οποιαδήποτε έκφραση που αξιολογείται σε μια boolean (λογική τιμή):

julia> n = 22;
julia> @assert isodd(n) "n must be odd"
ERROR: AssertionError: n must be odd

try...catch

Κάποια σφάλματα είναι αναγκαστικά μοιραία, αλλά συχνά περιμένουμε το πρόγραμμα να ανακάμψει ομαλά.

Από προεπιλογή, ένα σφάλμα τερματίζει αμέσως την τρέχουσα συνάρτηση και το σφάλμα (μαζί με οποιοδήποτε ενημερωτικό μήνυμα) περνά στη συνάρτηση που την κάλεσε.

Αυτό συνεχίζεται προς τα πάνω στη στοίβα κλήσεων, μέχρι ο κώδικας του ανώτατου επιπέδου να τερματιστεί με ένα μήνυμα σφάλματος.

Σε οποιοδήποτε στάδιο, το σφάλμα μπορεί να πιαστεί με ένα μπλοκ try...catch που προσπαθεί να το χειριστεί.

julia> n = -1;
julia> try
           log_n = log(n)
       catch problem
           if problem isa DomainError # number out of range
               # See next section for more on @warn and @info
               @warn "you may have supplied a negative real number: $n"
               @info "trying with complex argument"
               log_n = log(Complex(n))  # fallback calculation

           elseif problem isa MethodError # no idea what n is
               @error "please supply a valid argument"
 
           else
              rethrow() # the error could be anything else
           end
      end
┌ Warning: you may have supplied a negative real number: -1
└ @ Main REPL[3]:5
[ Info: trying with complex argument
0.0 + 3.141592653589793im  # success

Στο παραπάνω παράδειγμα, η log(n) χρειάζεται το n να είναι είτε μια θετική πραγματική τιμή είτε οποιαδήποτε μιγαδική τιμή. Το try ... catch παγιδεύει προβλήματα με αρνητικές πραγματικές τιμές, επιστρέφοντας τη σωστή μιγαδική απάντηση iπ σε μαθηματικό συμβολισμό.

Αν δώσεις, για παράδειγμα, ένα όρισμα συμβολοσειράς, δεν υπάρχει τρόπος ανάκαμψης παρά μόνο να ζητήσεις από τον χρήστη να το διορθώσει.

Ως τελευταία γενική λύση, προσθέσαμε το rethrow() για οτιδήποτε δεν είναι ούτε DomainError ούτε MethodError.

Σημείωση: Μερικές φορές ένα try...catch είναι αυτό που χρειάζεσαι, αλλά απόφυγε να το παρακάνεις. Αν μπορείς να χρησιμοποιήσεις αντ' αυτού ένα μπλοκ if...else, θα είναι πολύ πιο αποδοτικό από το να πιάνεις εξαιρέσεις.

Καταγραφή

Πρόσεξε ότι η συνάρτηση error(), που συζητήσαμε παραπάνω, δεν πρέπει να συγχέεται με τη μακροεντολή @error.

Η συνάρτηση δημιουργεί μια εξαίρεση, η οποία θα περάσει προς τα πάνω στη στοίβα κλήσεων αν δεν πιαστεί.

Η μακροεντολή @error, μαζί με τις αντίστοιχες @debug, @info και @warn, είναι μέρος του module Logging και προορίζεται να παράγει ενημερωτικά μηνύματα χωρίς να αλλάζει τη ροή του προγράμματος.

Η έξοδος πηγαίνει στο τερματικό από προεπιλογή (με χρώματα ανάλογα με τη σοβαρότητα), αν και σε μια πραγματική εφαρμογή υπάρχουν πολλές άλλες δυνατότητες.

julia> @warn "Something looks not quite right"
┌ Warning: Something looks not quite right
└ @ Main REPL[55]:1

julia> @error "Panic!"
┌ Error: Panic!
└ @ Main REPL[56]:1

Δες επίσης το προηγούμενο παράδειγμα, στην ενότητα try...catch.

Οδηγίες

Η Έλενα είναι η νέα υπεύθυνη ποιότητας ενός εργοστασίου εφημερίδων. Μόλις έχει έρθει στην εταιρεία, αποφάσισε να εξετάσει κάποιες από τις διαδικασίες του εργοστασίου για να δει τι θα μπορούσε να βελτιωθεί. Ανακάλυψε ότι οι τεχνικοί κάνουν πολλούς ποιοτικούς ελέγχους με το χέρι. Βλέπει ότι υπάρχει μια καλή ευκαιρία για αυτοματοποίηση και σου ζητάει, ως ελεύθερος επαγγελματίας προγραμματιστής, να αναπτύξεις ένα λογισμικό που θα παρακολουθεί μερικά από τα μηχανήματα.

1. Έλεγξε το επίπεδο υγρασίας της αίθουσας

Η πρώτη σου αποστολή είναι να γράψεις ένα λογισμικό που θα παρακολουθεί το επίπεδο υγρασίας της αίθουσας παραγωγής. Υπάρχει ήδη ένας αισθητήρας συνδεδεμένος με το λογισμικό της εταιρείας, που επιστρέφει περιοδικά το ποσοστό υγρασίας της αίθουσας.

Πρέπει να υλοποιήσεις μια συνάρτηση στο λογισμικό που θα πετάει ένα σφάλμα αν το ποσοστό υγρασίας είναι πολύ υψηλό. Αν η υγρασία βρίσκεται σε αποδεκτό επίπεδο, θα προστεθεί ένα log Info. Η συνάρτηση θα πρέπει να ονομάζεται humiditycheck και να δέχεται το ποσοστό υγρασίας ως όρισμα.

Θα πρέπει να τερματίσεις με ένα ErrorException (το ακριβές μήνυμα δεν έχει σημασία, αλλά πρέπει να περιέχει το μετρημένο επίπεδο υγρασίας) αν το ποσοστό ξεπερνά το 70%. Διαφορετικά, πρόσθεσε ένα log Info, με το μήνυμα "humidity level check passed: h%", όπου h είναι το ποσοστό υγρασίας.

julia> humiditycheck(60)
[ Info: humidity level check passed: 60%
julia> humiditycheck(100)
ERROR: humidity check failed: 100%

2. Έλεγξε για υπερθέρμανση

Η Έλενα είναι πολύ ευχαριστημένη με την πρώτη σου εργασία και σου ζητάει να ασχοληθείς με την παρακολούθηση της θερμοκρασίας των μηχανημάτων. Καθώς συζητάς με έναν τεχνικό, τον Greg, σου λέει ότι αν η θερμοκρασία ενός μηχανήματος ξεπερνά τους 500°C, οι τεχνικοί αρχίζουν να ανησυχούν για υπερθέρμανση.

Το μηχάνημα είναι εξοπλισμένο με έναν αισθητήρα που μετρά την εσωτερική του θερμοκρασία. Πρέπει να ξέρεις ότι ο αισθητήρας είναι πολύ ευαίσθητος και χαλάει συχνά. Σε αυτή την περίπτωση, οι τεχνικοί θα χρειαστεί να τον αλλάξουν.

Η δουλειά σου είναι να υλοποιήσεις μια συνάρτηση temperaturecheck που δέχεται τη θερμοκρασία ως όρισμα και είτε προσθέτει ένα log αν όλα είναι καλά είτε πετάει ένα σφάλμα αν ο αισθητήρας είναι χαλασμένος ή αν το μηχάνημα αρχίζει να υπερθερμαίνεται. Γνωρίζοντας ότι αργότερα θα χρειαστεί να αντιδράσεις διαφορετικά ανάλογα με το σφάλμα, χρειάζεσαι έναν μηχανισμό για να ξεχωρίζεις τα δύο είδη σφαλμάτων.

  • Αν ο αισθητήρας είναι χαλασμένος, η θερμοκρασία θα είναι nothing. Σε αυτή την περίπτωση, θα πρέπει να τερματίσεις με ένα ArgumentError (το μήνυμα δεν έχει σημασία).
  • Όταν ο αισθητήρας λειτουργεί, αν η θερμοκρασία ξεπερνά τους 500°C, θα πρέπει να πετάξεις ένα DomainError που περιλαμβάνει τη μετρημένη θερμοκρασία.
  • Διαφορετικά, όλα είναι καλά, οπότε πρόσθεσε ένα log Info με το μήνυμα "temperature check passed: t °C", όπου t είναι η θερμοκρασία.
julia> temperaturecheck(nothing)
ERROR: ArgumentError: sensor is broken

julia> temperaturecheck(800)
ERROR: DomainError with 800:
"overheating detected"

julia> temperaturecheck(500)
[ Info: temperature check passed: 500 °C

3. Όρισε προσαρμοσμένο σφάλμα

Για την επόμενη εργασία, θα χρειαστεί να ορίσεις ένα πιο γενικό σφάλμα που να τα πιάνει όλα. Οι λεπτομέρειες της υλοποίησης δεν έχουν σημασία, αρκεί να είναι ένα σφάλμα και το όνομά του να είναι MachineError. Μπορείς ελεύθερα να συμπεριλάβεις πεδία και μηνύματα όπως σε βολεύουν.

4. Παρακολούθησε το μηχάνημα

Τώρα που το μηχάνημά σου μπορεί να εντοπίζει σφάλματα και έχεις ένα προσαρμοσμένο σφάλμα μηχανήματος, προσθέτεις μια συνάρτηση-περιτύλιγμα που μπορεί να αναφέρει πώς λειτουργούν όλα. Πέρα από την επιστροφή των logs από τις προηγούμενες συναρτήσεις, αυτό το περιτύλιγμα θα χρειαστεί επίσης να προσθέτει logs ανάλογα με τον τύπο ή τους τύπους αποτυχιών που συμβαίνουν.

  • Έλεγξε την υγρασία και τη θερμοκρασία.
  • Αν ο έλεγχος υγρασίας πετάει ένα ErrorException, θα πρέπει να προστεθεί ένα log Error με το μήνυμα "humidity level check failed: h%", όπου h είναι το ποσοστό υγρασίας.
  • Αν ο έλεγχος θερμοκρασίας πετάει ένα ArgumentError, θα πρέπει να προστεθεί ένα log Warn με το μήνυμα "sensor is broken".
  • Αν ο έλεγχος θερμοκρασίας πετάει ένα DomainError, θα πρέπει να προστεθεί ένα log Error με το μήνυμα "overheating detected: t °C", όπου t είναι η θερμοκρασία.
  • Αν ένας από τους δύο ελέγχους ή και οι δύο αποτύχουν, θα πρέπει να πεταχτεί ένα μόνο MachineError αφού προστεθούν τα logs.
  • Αν όλα είναι καλά, θα προστεθούν μόνο τα logs από τις humiditycheck και temperaturecheck.

Υλοποίησε μια συνάρτηση machinemonitor() που δέχεται την υγρασία και τη θερμοκρασία ως ορίσματα.

julia> machinemonitor(42, 450)
[ Info: humidity level check passed: 42%
[ Info: temperature check passed: 450 °C

julia> machinemonitor(42, 550)
[ Info: humidity level check passed: 42%
┌ Error: overheating detected: 550 °C
└ @ Main # output truncated

Error: MachineError

julia> machinemonitor(82, 521)
┌ Error: humidity level check failed: 82%
└ @ Main # output truncated
┌ Error: overheating detected: 521 °C
└ @ Main # output truncated

Error: MachineError

julia> machinemonitor(42, nothing)
[ Info: humidity level check passed: 42%
┌ Warning: sensor is broken
└ @ Main # output truncated

Error: MachineError
Επεξεργασία μέσω GitHub Ο σύνδεσμος ανοίγει σε νέο παράθυρο ή καρτέλα
Julia Exercism

Έτοιμος να ξεκινήσεις την άσκηση Αισθητήρες εργοστασίου;

Γράψου στο Exercism για να μάθεις και να κατακτήσεις Julia με 35 έννοιες128 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.