Üdvözöljük a sorozat második részében (az első részt "itt" találja).
Továbbra is megvizsgálunk néhány más rendkívül érdekes drágakövet, amelyeket a "Mutant" használ.
Kezdetben azt terveztem, hogy több gyöngyszemet is lefedek, de miután elkezdtem kutatni az IceNine gyöngyszembe, rájöttem, hogy ez elég nagy egy saját blogbejegyzéshez.
"IceNine"
A IceNine
egy könyvtár az objektumok mélyhűtéséhez:
Mire kell ez?
Az elmúlt években a „funkcionális programozás” divattá vált, és hogyan lehet bevezetni olyan nyelveken, amelyek valójában nem működnek, mint a Ruby.
Attól függően, hogy kit kérdezel, mindenki másképp határozza meg, hogy mit jelent a „funkcionális”. Én, két tulajdonságom van, amelyeket alapvetőnek tartok egy funkcionális nyelvhez:
- „Tiszta funkciók”
- Változhatatlan adatszerkezetek
Úgy gondolom, hogy a Rubynak ebben az értelemben soha nem lesznek tiszta függvényei, de megváltoztathatatlan adatstruktúrákat hozhat létre a Rubyban a freeze
meghívásával. Azonban pusztán a freeze
meghívása egy objektumon nem fagyasztja le azt, ahogyan azt a fenti kezdeti kódmintában teheti.
És itt jön be a IceNine
.
Előfordulhat, hogy a megváltoztathatatlan adatstruktúrák nem alkalmazhatók mindenhol és mindig, de gyakran jelentős előnyöket kínálnak a változtatható adatstruktúrákhoz képest, például:
- Sokkal könnyebb egy adatszerkezetről okoskodni, ha tudod, hogy mindig garantált állapota lesz
- Kevésbé hajlamos a hibákra. A legfurcsább hibák gyakran azért fordulnak elő, mert az adatok olyan módon változtak, amit nem is sejtett
- Könnyebbé teszi a párhuzamos kód írását, mivel a megváltoztathatatlan adatstruktúrák számos gyakori buktatót kiküszöbölnek a párhuzamos kód írása során
Az indoklás nélkül nézzük meg, hogyan működik!
A megfelelő fagyasztó megtalálása
A fenti példámban láthatta, hogy a IceNine
használata a következőkre vezethető vissza:
Íme a IceNine
modul megfelelő része (a lib/ice_nine.rb
-ben van meghatározva):
Rendben, itt nincs sok látnivaló, csak delegálunk IceNine::Freezer
-ra (a lib/ice_nine/freezer.rb
-ben van meghatározva):
Hagyjuk most figyelmen kívül ezt a rekurziós őrző dolgot – erről később részletesen fogunk beszélni –, és menjünk tovább a spirálon:
Ott van. Itt történik a varázslat.
Boncoljuk ezt a sort:
Először is meg kell értenünk:
A object
az az objektum, amelyet az elején átadtunk a IceNine.deep_freeze
-nek:
Tehát a object
lenne
és így object.class
Hash
lenne.
És hogy néz ki a []
módszer?
Most kezd érdekessé válni a dolog. Ez a @freezer_cache
a fájl tetején van definiálva:
A fenti kód tömör, és sok munkát végez:
- A
@freezer_cache
-et hashként állítottuk be. Vegye figyelembe, hogy a@freezer_cache
nem egy példányváltozó, hanem egy "osztálypéldányváltozó", ami azt jelenti, hogy ez a hozzárendelés akkor hajtódik végre, amikor a Ruby beolvassa aFreezer
osztályt, mivel a Rubys "az osztálytestek végrehajthatók". - Elhaladunk egy blokkon a
Hash#new
felé. Ha olyan kulcsot kérünk a gyorsítótárban, amely nem létezik, ez a blokk a hash objektummal és a kulccsal együtt lesz meghívva, és vissza kell adnia az alapértelmezett értéket (lásd a "Kivonatoló dokumentumok" című részt). - Tehát a
cache
blokkparaméter maga a hash, amod
pedig aIceNine
-nak átadott adatstruktúra osztálya, tehát ittHash
. - Ezután felfelé haladunk az ősláncon, és keresünk egy megfelelő fagyasztót. Ha ragaszkodunk a hash-példához, azonnal megtaláljuk a
IceNine::Freezer::Hash
értéket, mivel bármely osztály ősláncának első láncszeme… maga az osztály, ahogy itt látható:
Röviden: Hash
adott esetben ez meglepetést okozhat! - Hash
fagyasztó. Nyilvánvalóan ez itt a lehető legegyszerűbb példa, mivel sok szélsőséges eset van, ahol ez nem olyan egyszerű. Itt azonban nem megyek bele a részletekbe, és inkább a magas szintre koncentrálok (a find
megjelenésének részleteibe sem megyek bele, mivel az egy kicsit bonyolultabb, de megnézheti a lib/ice_nine/freezer
-ben).
Most, miután alaposan megértette a fagyasztó keresés működését, térjünk vissza
és főleg:
Mélyhűtő
Ezt most már tudjuk
rész, tehát nézzük azt a részt:
és képzeld el azt
egy IceNine::Freezer::Hash
-at adott volna vissza, ahogy a fenti bekezdésben tettük.
Így néz ki a IceNine::Freezer::Hash.guarded_deep_freeze
:
Nézzük meg, miről is szól a freeze_key_value_pairs
, mielőtt megnéznénk, mi történik a super
-on keresztül:
Oké, ez elég egyszerű, nem? Iteráljuk a hash összes kulcsát és értékét, és mélyhűtjük is őket.
Most mi a helyzet super
-el?
Ahogy fentebb látható a kezdeti IceNine::Freezer::Hash
kódrészletemben, ez az osztály a IceNine::Freezer::Object
-tól örökli:
Most már kezdenek értelmet nyerni a dolgok:
- Ellenőrizzük, hogy a kérdéses objektum támogatja-e a
freeze
-et, és ha igen, akkor lefagyasztjuk. - Ez a legelső példám első részét fedi le
- Ezután lefagyasztjuk az objektum összes további attribútumait, a
Hash
ay kulcsok és értékek esetén
Oké, akkor most már tudjuk:
- hogyan keresnek egy megfelelő fagyasztóosztályt. A fenti példákban a
IceNine::Freezer::Hash
- hogyan végzi el ez az osztály a tényleges fagyasztást.
Így már csak egy dolgot kell megmagyarázni: mi van ezzel a rekurziós őrrel?
A rekurziós őr
Ha odafigyelt, észrevette, hogy a IceNine
rekurzív módon bejár minden olyan adatstruktúrát, amelyet átad.
Ne feledje, hogy így kezdtük IceNine::Freezer
-ben:
Ezután újraindítottuk az objektumok osztályának lekérését, a megfelelő fagyasztó meghatározását, lefagyasztását és így tovább a IceNine::Freezer::Hash
-ben:
Tehát ott van a rekurzió, amelyet a rekurziós őrön keresztül indítunk el.
Miért van az, hogy „őr”? Mert létrehozhat és átadhat ilyen ciklikus adatstruktúrákat:
Rekurziós védő nélkül IceNine
addig ismétlődik, amíg meg nem kapod
Nézzük meg a IceNine::RecursionGuard::ObjectSet
(definiálva: lib/ice_nine/support/recursion_guard
):
A fenti kód hihetetlenül egyszerű és ugyanakkor hihetetlenül erős.
Először megkapjuk a vizsgált objektum object_id
értékét:
Most a fontos rész: Ha már láttuk ezt az objektumot, az azt jelenti, hogy valóban egy ciklikus struktúrán haladunk át. Ebben az esetben csak visszatérünk:
Ha idáig eljutottunk, akkor először látjuk ezt az objektumot, ezért tároljuk' object_id
:
És végül engedünk a blokknak:
A deep_freeze
elérhetővé tétele minden objektum számára
A IceNine
egy alapkiterjesztést is kínál a lib/ice_nine/core_ext/object.rb
nyelven:
Először egy külön modult definiálunk, követve azt a konvenciót, hogy az alapvető osztályokba kevert modulok az core_ext
hatókörben vannak definiálva. Ez a modul nagyon egyszerű, csak meghívja az IceNine.deep_freeze
-t, majd átadja neki az self
-t. Mi lesz self
? A példánymódszerekkel rendelkező modulok nem használhatók önállóan, csak mixinként. Azt jelenti, osztályba keverve. Tehát az self
lesz az az osztály, amelybe kevertük.
Hogyan aktiváljuk ezt?
By
Ez gyakorlatilag elérhetővé teszi a deep_freeze
-ot a Ruby objektumterében lévő összes objektum számára.
Egyébként nem számít, hogy ebben az esetben az class_eval
vagy instance_eval
értéket használja.
Mindkét
és
itt is ugyanaz az eredmény. Ez azonban óriási különbséget jelentene, ha az deep_freeze
értéket a Object
-on közvetlenül a def
-en keresztül határozná meg.
Például. ez
a deep_freeze
értéket egy példány szinten határozza meg, mivel a class_eval
csak újra megnyitja az osztályt. Szóval jók vagyunk.
Ezt azonban
nem működne, mivel egy egyszeres metódust hozna létre (értsd: osztálymetódus), nem pedig példánymetódusokat.
Tehát tisztában kell lennie ezekkel a különbségekkel. A legtöbb drágakő, amit láttam, használja a Object.instance_eval
trükköt a core_ext
-hez, és valószínűleg ezt a mintát kell követnie a saját drágaköveinél.
Becsomagolva
Tehát foglaljuk össze, mit tanultunk ma a IceNine
-ról:
- rekurzívan bejár minden olyan adatstruktúrát, amelyen átjut
- rekurzióvédőt használva, hogy megakadályozza a ciklikus adatstruktúra ismétlődését
- gyorsítótárazott leképezést tart fenn arról, hogy milyen fagyasztót milyen adattípushoz kell használni
- lefagyasztja a kapott adatokat
- majd mélyebbre megy a spirálban minden gyermek számára
A sorozat egy részében ennyi. A következő 3. részben az „Adamantium gem”-et és az „abstract_type gem”-et fogom nézni.