A programozók többnyire tökéletes szoftvert igyekeznek írni, és többnyire kudarcot vallanak.
A dolgok váratlanul elromlanak, és tudnunk kell kezelni az ilyen helyzeteket.
Egyes nyelvtervezők úgy gondolják, hogy a legfontosabb a hiba minél gyorsabb felismerése, majd a végrehajtás leállítása egy informatív üzenettel a hibakeresés segítésére.
Az adattudományi nyelvek hajlamosak árnyaltabb megközelítést választani. Egyes hibák olyan súlyosak, hogy az azonnali leállás szükséges, de gyakran jobb egy problémát később kezelendő dologként megjelölni, majd folytatni a végrehajtást.
A Semmisség fogalomban láttuk, hogy a Julia különféle helyőrzőket biztosít problémás értékekhez: missing, NaN és Inf.
Az, hogy ezek jobb megközelítést jelentenek-e a program leállításánál egy adott helyzetben, a programozó megítélésének kérdése.
Egy megjegyzés a nevezéktanról mielőtt belemegyünk a részletekbe: a Julia dokumentációja a „hiba” és a „kivétel” szavakat nagyrészt felcserélhetőnek tekinti. Az alábbi tartalom is lehet ugyanilyen következetlen.
A tantervben eddig eljutva már biztosan láttál jó néhány hibaüzenetet a Juliától. Például:
julia> Int(3.14)
ERROR: InexactError: Int64(3.14)
Ha egy tizedes törtet egész számmá alakítunk, az pontosságvesztéssel jár, ezért InexactError hibát kapunk.
Az InexactError egy típus, amely a Juliába szabványosként beépített számos hiba egyike (jelenleg 25).
Mind az Exception altípusai:
julia> supertype(InexactError)
Exception
throw()A szabványos hibatípusok némelyike hasznos lehet, ha a saját kódodban szeretnéd előidézni.
Mint minden konkrét típusnak, a hibáknak is vannak konstruktoraik. Különféle argumentumokat fogadnak, ezért nézd meg a dokumentációt ahhoz, amelyiket használni szeretnéd.
julia> DomainError(42, "out of range")
DomainError(42, "out of range")
A hiba használatához csomagold a konstruktort egy throw() függvénybe:
julia> throw(DomainError(42, "out of range"))
ERROR: DomainError with 42:
out of range
error()Ha gyorsan, kevésbé elegánsan akarsz megoldani valamit, az error() függvény kényelmes lehet.
Egy stringet (vagy egy string összetevőit) vesz argumentumként:
julia> happy = false;
julia> happy || error("😞 something went wrong")
ERROR: 😞 something went wrong
Új hibatípusokat létrehozni elvileg nagyon egyszerű.
Egyszerűen vedd fel az Exception egy újabb altípusát:
julia> struct MyError <: Exception end
julia> throw(MyError)
ERROR: MyError
Az állítás alapgondolata: „Ennek az állításnak igaznak kell lennie, ezért jelezd hangosan, ha hamis.” Ennek értéke főleg a hibakeresés során mutatkozik meg, mivel az éles kódnak soha nem szabad elbuknia egy állításon.
A Típusok fogalomban láttuk, hogy típusállításokat adhatunk hozzá, például egy függvény visszatérési típusának ellenőrzéséhez.
julia> 42::Number
42
julia> "two"::Number
ERROR: TypeError: in typeassert, expected Number, got a value of type String
Általánosabban a @assert makróval bármilyen olyan kifejezést tesztelhetünk, amely Boolean értéket ad:
julia> n = 22;
julia> @assert isodd(n) "n must be odd"
ERROR: AssertionError: n must be odd
try...catch
Egyes hibák szükségszerűen végzetesek, de gyakran azt várjuk, hogy a program kecsesen helyreálljon.
Alapértelmezés szerint egy hiba azonnal leállítja az aktuális függvényt, és a hiba (az esetleges informatív üzenettel együtt) átadódik a hívó függvénynek.
Ez folytatódik a hívási vermen felfelé, amíg a legfelső szintű kód le nem áll egy hibaüzenettel.
Bármelyik szakaszban elkapható a hiba egy try...catch blokkal, amely megpróbálja kezelni.
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
A fenti példában a log(n) függvényhez n-nek vagy pozitív valós értéknek, vagy bármilyen komplex értéknek kell lennie.
A try ... catch elkapja a negatív valós értékekkel kapcsolatos problémákat, és visszaadja a helyes komplex választ (iπ) matematikai jelöléssel.
Ha például egy string argumentumot adsz meg, nincs helyreállás, csak ha megkéred a felhasználót, hogy javítsa ki.
Végső mentőhálóként hozzáadtuk a rethrow() hívást minden olyan esethez, amely sem DomainError, sem MethodError.
Megjegyzés: Néha a try...catch az, amire szükséged van, de kérlek, kerüld a túlzott használatát.
Ha helyette egy if...else blokk használható, az sokkal hatékonyabb lesz, mint a kivételek elkapása.
Ne keverd össze a fentebb tárgyalt error() függvényt a @error makróval.
A függvény kivételt hoz létre, amely elkapás híján felkerül a hívási vermen.
A @error makró, valamint a @debug, @info és @warn társai a Logging modul részei, és céljuk, hogy informatív üzeneteket hozzanak létre anélkül, hogy megváltoztatnák a program futását.
A kimenet alapértelmezés szerint a terminálra kerül (a súlyosság szerint színezve), bár egy valódi alkalmazásban sok más lehetőség is van.
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
Lásd még a korábbi példát a try...catch alatt.
Elena egy újságüzemben az új minőségügyi vezető. Mivel épp most érkezett a céghez, úgy döntött, hogy átnézi a gyár néhány folyamatát, hogy lássa, min lehetne javítani. Felfedezte, hogy a technikusok rengeteg minőségellenőrzést végeznek kézzel. Úgy látja, jó lehetőség kínálkozik az automatizálásra, ezért megkér téged, egy szabadúszó fejlesztőt, hogy fejlessz egy szoftvert néhány gép felügyeletére.
Az első feladatod, hogy írj egy szoftvert, amely a gyártóterem páratartalmát felügyeli. A cég szoftveréhez már csatlakozik egy érzékelő, amely rendszeresen visszaadja a helyiség páratartalmát százalékban.
A szoftverben meg kell valósítanod egy függvényt, amely hibát dob, ha a páratartalom túl magas.
Ha a páratartalom elfogadható szinten van, egy Info naplóbejegyzés kerül hozzáadásra.
A függvény neve humiditycheck legyen, és argumentumként a páratartalmat kapja meg.
Ha a százalék meghaladja a 70%-ot, állj le egy ErrorException hibával (a pontos üzenet nem fontos, de tartalmaznia kell a mért páratartalmat).
Egyébként adj hozzá egy Info naplóbejegyzést a "humidity level check passed: h%" üzenettel, ahol a h a páratartalom százaléka.
julia> humiditycheck(60)
[ Info: humidity level check passed: 60%
julia> humiditycheck(100)
ERROR: humidity check failed: 100%
Elena nagyon elégedett az első feladatoddal, és megkér, hogy foglalkozz a gépek hőmérsékletének felügyeletével. Miközben egy technikussal, Greggel beszélgetsz, megtudod, hogy ha egy gép hőmérséklete meghaladja az 500 °C-ot, a technikusok aggódni kezdenek a túlmelegedés miatt.
A gépet egy érzékelővel szerelték fel, amely a belső hőmérsékletét méri. Tudd, hogy az érzékelő nagyon érzékeny, és gyakran elromlik. Ilyenkor a technikusoknak ki kell cserélniük.
A feladatod, hogy megvalósíts egy temperaturecheck függvényt, amely argumentumként a hőmérsékletet kapja, és vagy naplóbejegyzést ad hozzá, ha minden rendben van, vagy hibát dob, ha az érzékelő elromlott, illetve ha a gép túlmelegedni kezd.
Mivel később a hiba típusától függően másképp kell majd reagálnod, szükséged lesz egy mechanizmusra, amellyel meg tudod különböztetni a kétféle hibát.
nothing lesz.
Ebben az esetben állj le egy ArgumentError hibával (az üzenet nem fontos).DomainError hibát, amely tartalmazza a mért hőmérsékletet."temperature check passed: t °C" üzenettel, ahol a t a hőmérséklet.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
A következő feladathoz definiálnod kell egy általánosabb, mindent elkapó hibát.
A megvalósítás részletei nem fontosak azon túl, hogy hiba legyen, és hogy a neve MachineError legyen.
Nyugodtan adhatsz hozzá mezőket és üzeneteket, ha az segít.
Most, hogy a géped fel tudja ismerni a hibákat, és van egy egyéni MachineError hibád, hozzáadsz egy burkolófüggvényt, amely beszámol róla, hogy minden hogyan működik.
Amellett, hogy visszaadja a korábbi függvények naplóbejegyzéseit, ennek a burkolófüggvénynek a fellépő hiba típusától (típusaitól) függően további naplóbejegyzéseket is hozzá kell adnia.
ErrorException hibát dob, egy Error naplóbejegyzést kell hozzáadni a "humidity level check failed: h%" üzenettel, ahol a h a páratartalom százaléka.ArgumentError hibát dob, egy Warn naplóbejegyzést kell hozzáadni a "sensor is broken" üzenettel.DomainError hibát dob, egy Error naplóbejegyzést kell hozzáadni a "overheating detected: t °C" üzenettel, ahol a t a hőmérséklet.MachineError hibát kell dobni.humiditycheck és a temperaturecheck naplóbejegyzései kerülnek hozzáadásra.Valósítsd meg a machinemonitor() függvényt, amely argumentumként a páratartalmat és a hőmérsékletet kapja.
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
Iratkozz fel az Exercism-re, hogy megtanuld és elsajátítsd a(z) Julia nyelvet 35 fogalom128 feladat segítségével, valódi emberi mentorálással, mindez ingyen.