Skip to content
Menu
CDhistory
CDhistory

Kötések és lexikális hatókör a Rubyban

Posted on november 10, 2021 by admin

Boldog új évet, és üdvözöljük újra a Ruby Magicben! Ebben a téli epizódban a kötésekbe és a hatókörökbe merülünk el. Szóval húzzuk fel a síléceket, és kövessünk minket mélyen az erdőbe.

Legutóbb a lezárásokat vizsgáltuk a Rubyban, összehasonlítva a blokkokat, a procokat és a lambdákat. A három típus közötti különbségeken kívül érintettük, hogy mi határozza meg a zárlatokat.

A zárlat egy első osztályú függvény egy környezettel. A környezet egy leképezés azokra a változókra, amelyek a closure létrehozásakor léteztek. A closure megtartja a hozzáférést ezekhez a változókhoz, még akkor is, ha azok egy másik hatókörben vannak definiálva.

Felfedeztük a Ruby első osztályú függvények megfelelőjét, de kényelmesen átugrottuk a környezeteket. Ebben az epizódban megnézzük, hogyan működik ez a környezet a lezárások, osztályok és osztálypéldányok esetében, megvizsgálva, hogyan kezeli a Ruby a lexikai hatóköröket a kötéseken keresztül.

  • Lexikai hatókör
  • Öröklött hatókörök és Proc-kötések
  • Megkötések
  • Egy példa a való életből
  • Elvesztettük az erdőben?

Lexikai hatókör

A programozásban a hatókör a kód egy adott részén elérhető kötésekre utal. A kötés vagy névkötés egy nevet köt egy memóriahivatkozáshoz, mint ahogy egy változó neve az értékéhez. A hatókör határozza meg, hogy mit self jelent, milyen metódusok hívhatók, és milyen változók állnak rendelkezésre.

A Ruby, mint a legtöbb modern programozási nyelv, statikus hatókört használ, amelyet gyakran lexikális hatókörnek neveznek (szemben a dinamikus hatókörrel). Az aktuális hatókör a kód szerkezetén alapul, és meghatározza a kód bizonyos részein elérhető változókat. Ez azt jelenti, hogy a hatókör megváltozik, amikor a kód metódusok, blokkok és osztályok között ugrál – mivel ezek mind különböző helyi változókkal rendelkezhetnek például.

1 2 3 4 5 6
def bar foo = 1 foo end bar # => 1 

Ezzel a módszerrel létrehozunk egy helyi változót egy metóduson belül, és kiírjuk a konzolra. A változó a metóduson belül hatókörben van, mivel ott hozzuk létre.

1 2 3 4 5 6 7
foo = 1 def bar foo end bar # => NameError (undefined local variable or method `foo' for main:Object) 

Ebben a példában a változót a metóduson kívül hozzuk létre. Ha a változót a metóduson belül hívjuk meg, hibát kapunk, mivel a változó hatókörön kívül van. A helyi változók szűk hatókörűek, ami azt jelenti, hogy egy metódus nem tud hozzáférni egy önmagán kívül eső változóhoz, hacsak nem adjuk át argumentumként.

1 2 3 4 5 6 7
@foo = 1 def bar @foo end bar # => 1 

Míg a helyi változók lokálisan elérhetők, a példányváltozók egy osztálypéldány minden metódusa számára elérhetők.

Öröklött hatókörök és Proc-kötések

Amint azt az előző példákban láttuk, a hatókör a kódban elfoglalt helytől függ. Egy metóduson kívül definiált helyi változó nincs hatókörben a metóduson belül, de elérhetővé tehető, ha példányváltozóvá alakítjuk. A metódusok nem férhetnek hozzá a rajtuk kívül definiált helyi változókhoz, mert a metódusoknak saját hatókörük van, saját kötöttségekkel.

A folyamatok (beleértve a blokkokat és a lambdákat is, kiterjesztésképpen) másképp vannak. Valahányszor egy procot instanciálunk, létrejön egy kötés, amely örökli a blokk létrehozásának kontextusában lévő helyi változókra való hivatkozásokat.

1 2
foo = 1 Proc.new { foo }.call # => 1 

A példában a foo nevű változót 1-re állítjuk. Belsőleg a második sorban létrehozott Proc objektum új kötést hoz létre. A proc meghívásakor lekérdezhetjük a változó értékét.

Mivel a kötés a proc inicializálásakor jön létre, nem hozhatjuk létre a proc-ot a változó definiálása előtt, még akkor sem, ha a blokkot csak a változó definiálása után hívjuk meg.

1 2 3
proc = Proc.new { foo } foo = 1 proc.call # => NameError (undefined local variable or method `foo' for main:Object) 

A proc meghívása NameError eredményez, mivel a változó nincs definiálva a proc kötéseiben. Ezért a proc-ban elért változókat a proc létrehozása vagy argumentumként való átadása előtt definiálni kell.

1 2 3 4
foo = 1 proc = Proc.new { foo } foo = 2 proc.call # => 2 

A változót azonban megváltoztathatjuk, miután a fő kontextusban definiáltuk, mivel a proc kötése a másolás helyett hivatkozást tart rá.

1 2 3
foo = 1 Proc.new { foo = 2 }.call foo #=> 2 

Ebben a példában láthatjuk, hogy a foo változó ugyanarra az objektumra mutat a proc-ban, mint azon kívül. A proc-on belül frissíthetjük, hogy a proc-on kívüli változó is frissüljön.

Megkötések

A Ruby az aktuális hatókör nyomon követésére kötéseket használ, amelyek a kód minden egyes pozíciójában a végrehajtási kontextust kapszulázzák. A binding metódus egy Binding objektumot ad vissza, amely az aktuális pozícióban lévő kötéseket írja le.

1 2
foo = 1 binding.local_variables # => 

A kötésobjektumnak van egy #local_variables nevű metódusa, amely az aktuális hatókörben elérhető összes helyi változó nevét adja vissza.

1 2
foo = 1 binding.eval("foo") # => 1 

A kötésen a #eval metódus segítségével lehet kódot kiértékelni. A fenti példa nem túl hasznos, mivel a foo egyszerű hívása ugyanezt az eredményt adná. Mivel azonban a kötés egy objektum, amely továbbadható, néhány érdekesebb dologra is felhasználható. Nézzünk egy példát.

Egy példa a való életből

Most, hogy a garázsunk biztonságában megismertük a kötéseket, mintha kivinnénk őket a lejtőkre, és játszanánk a hóban. Eltekintve attól, hogy a Ruby az egész nyelven belül használja a kötéseket, vannak olyan helyzetek, amikor a kötőobjektumokat explicit módon használjuk. Jó példa erre az ERB-Ruby templating rendszere.

1 2 3 4 5 6 7 8 9 10
require 'erb' x = 1 def y 2 end template = ERB.new("x is <%= x %>, y() returns <%= y %>, self is `<%= self %>`") template.result(binding) # => "x is 1, y() returns 2, self is `main`" 

Ebben a példában létrehozunk egy x nevű változót, egy y nevű metódust és egy ERB-sablont, amely mindkettőre hivatkozik. Ezután átadjuk az aktuális kötést a ERB#result-nak, amely kiértékeli a sablonban lévő ERB tageket, és egy stringet ad vissza a változókkal kitöltve.

Az ERB a Binding#eval segítségével kiértékeli az egyes ERB tagek tartalmát az átadott kötés hatókörében. Egy egyszerűsített megvalósítás, amely a fenti példában működik, így nézhet ki:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
class DiyErb def initialize(template) @template = template end def result(binding) @template.gsub(/<%=(.+?)%>/) do binding.eval() end end end x = 1 def y 2 end template = DiyErb.new("x is <%= x %>, y() returns <%= y %>, self is `<%= self %>`") template.result(binding) # => "x is 1, y() returns 2, self is `main`" 

A DiyErb osztály inicializáláskor egy sablonsztringet vesz át. A #result metódusa megkeresi az összes ERB taget, és a tartalmuk kiértékelésének eredményével helyettesíti őket. Ehhez az átadott kötésen meghívja a Binding#eval-t, az ERB címkék tartalmával.

Azzal, hogy a #result metódus hívásakor átadja az aktuális kötést, a eval hívások hozzáférhetnek a metóduson kívül, sőt az osztályon kívül definiált változókhoz is, anélkül, hogy azokat explicit módon át kellene adnunk.

Elvesztettük az erdőben?

Reméljük, jól érezték magukat az erdei sítúránkon. Elmélyedtünk a hatókörökben és a lezárásokban, miután szépítettük őket. Reméljük, nem veszítettünk el titeket az erdőben. Szólj nekünk, ha többet szeretnél megtudni a kötésekről, vagy bármilyen más Ruby témában szeretnél elmerülni.

Köszönjük, hogy követtél minket, és kérlek, rúgd le a havat a kötéseidről, mielőtt elhagyod őket a következő fejlesztőnek. Ha tetszenek ezek a varázslatos utazások, akkor érdemes feliratkoznod a Ruby Magicre, hogy e-mailben értesülj, ha körülbelül havonta egyszer új cikket teszünk közzé.

Vélemény, hozzászólás? Kilépés a válaszból

Az e-mail-címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük

Legutóbbi bejegyzések

  • Az Acela visszatért: New York vagy Boston 99 dollárért
  • OMIM bejegyzés – # 608363 – CHROMOSOME 22q11.2 DUPLICATION SYNDROME
  • Kate Albrecht szülei – Tudj meg többet apjáról Chris Albrechtről és anyjáról Annie Albrechtről
  • Temple Fork Outfitters
  • Burr (regény)

Archívum

  • 2022 február
  • 2022 január
  • 2021 december
  • 2021 november
  • 2021 október
  • 2021 szeptember
  • 2021 augusztus
  • 2021 július
  • 2021 június
  • 2021 május
  • 2021 április
  • DeutschDeutsch
  • NederlandsNederlands
  • SvenskaSvenska
  • DanskDansk
  • EspañolEspañol
  • FrançaisFrançais
  • PortuguêsPortuguês
  • ItalianoItaliano
  • RomânăRomână
  • PolskiPolski
  • ČeštinaČeština
  • MagyarMagyar
  • SuomiSuomi
  • 日本語日本語
©2022 CDhistory | Powered by WordPress & Superb Themes