Byla vydána beta verze openSUSE Leap 16. Ve výchozím nastavení s novým instalátorem Agama.
Devadesátková hra Brány Skeldalu prošla portací a je dostupná na platformě Steam. Vyšel i parádní blog autora o portaci na moderní systémy a platformy včetně Linuxu.
Lidi dělají divné věci. Například spouští Linux v Excelu. Využít je emulátor RISC-V mini-rv32ima sestavený jako knihovna DLL, která je volaná z makra VBA (Visual Basic for Applications).
Revolut nabídne neomezený mobilní tarif za 12,50 eur (312 Kč). Aktuálně startuje ve Velké Británii a Německu.
Společnost Amazon miliardáře Jeffa Bezose vypustila na oběžnou dráhu první várku družic svého projektu Kuiper, který má z vesmíru poskytovat vysokorychlostní internetové připojení po celém světě a snažit se konkurovat nyní dominantnímu Starlinku nejbohatšího muže planety Elona Muska.
Poslední aktualizací začal model GPT-4o uživatelům příliš podlézat. OpenAI jej tak vrátila k předchozí verzi.
Google Chrome 136 byl prohlášen za stabilní. Nejnovější stabilní verze 136.0.7103.59 přináší řadu novinek z hlediska uživatelů i vývojářů. Podrobný přehled v poznámkách k vydání. Opraveno bylo 8 bezpečnostních chyb. Vylepšeny byly také nástroje pro vývojáře.
Homebrew (Wikipedie), správce balíčků pro macOS a od verze 2.0.0 také pro Linux, byl vydán ve verzi 4.5.0. Na stránce Homebrew Formulae lze procházet seznamem balíčků. K dispozici jsou také různé statistiky.
Byl vydán Mozilla Firefox 138.0. Přehled novinek v poznámkách k vydání a poznámkách k vydání pro vývojáře. Řešeny jsou rovněž bezpečnostní chyby. Nový Firefox 138 je již k dispozici také na Flathubu a Snapcraftu.
Šestnáctý ročník ne-konference jOpenSpace se koná 3. – 5. října 2025 v Hotelu Antoň v Telči. Pro účast je potřeba vyplnit registrační formulář. Ne-konference neznamená, že se organizátorům nechce připravovat program, ale naopak dává prostor všem pozvaným, aby si program sami složili z toho nejzajímavějšího, čím se v poslední době zabývají nebo co je oslovilo. Obsah, který vytvářejí všichni účastníci, se skládá z desetiminutových
… více »
1. Metody, alias funkce
2. Třídy
2.1 Objektově orientovaný přístup
2.2 Definice tříd
2.3 Proměnné instance, inicializace
2.4 Přístupové metody
2.5 Řízení přístupu
2.6 Dědičnost
2.7 Proměnné třídy
Společně s tím, jak náš kód poroste, si brzy začneme všímat určitých úseků, které se budou v našem programu až příliš často opakovat. Zcela zjednodušeně, vytváříme-li například matematickou aplikaci, tak takové věci, jako je výpočet faktoriálu nebo třeba obvodu kruhu, využijeme poměrně často.
Dokola opisovat stále tentýž kód je nejen únavné, ale především nepraktické. Vymyslíme-li totiž v budoucnu efektivnější algoritmus nebo nedej bože zjistíme, že v něm máme chybu, budeme v důsledku odsouzeni k tomu důkladně projít celý zdrojový kód, který může čítat i desetitisíce řádků.
Moderní programovací jazyky (a Ruby není výjimkou) za tímto účelem nabízejí použití tzv. funkcí, kterým se v případě objektových jazyků říká metody. Obecný zápis definice metody vypadá takto:
def název(parametr_1, parametr_2, …) příkazy return návratová_hodnota end
Konvence říká, že názvy metod začínají malým písmenem. Definice se
uvozuje klíčovým slovem def
, v závorce následuje výčet
proměnných, jejichž prostřednictvím budou metodě předány hodnoty, vzájemně oddělené čárkou. V těle metody je zapsán samotný kód, za klíčovým slovem
return
je uvedena návratová hodnota. Definici uzavírá klíčové
slovo end
.
Ačkoli se metody obvykle uvádějí v definici tříd, jak si ostatně zanedlouho ukážeme, lze je uvádět i samostatně jako klasické funkce v procedurálních jazycích. V souboru se pak jejich definice zapisují před samotným kódem.
Pozapomeňme na chvíli na existenci vhodnější Math.sqrt()
a
zkusme si napsat metodu na výpočet druhé odmocniny ze zadaného čísla sami.
Ta by mohla vypadat například takto:
irb(main):001:0> def odmocnina(cislo) irb(main):002:1> return cislo**(1.0/2) irb(main):003:1> end => nil
K uvedení do chodu je třeba metodu odněkud z programu zavolat:
irb(main):004:0> odmocnina(256) => 16.0
Metody jsou volány svým jménem. V závorce jsou jim předány hodnoty,
které jsou následně přiřazeny lokálním proměnným v jejich těle – v
našem případě tedy platí, že cislo
= 256.
Jelikož metoda vrací hodnotu, lze ji postavit na pravou stranu přiřazení:
irb(main):005:0> vysledek = odmocnina(16) => 4.0 irb(main):006:0> puts vysledek 4.0 => nil
Pojďme si naši definici trochu rozšířit; řekněme, že chceme mít možnost počítat libovolnou odmocninu, nejen druhou. Toho zcela intuitivně docílíme přidáním druhého parametru. Zároveň by se nám ale líbilo, abychom tento parametr nemuseli uvádět v případě, že nám druhá odmocnina stačí. Jak na to?
Návrháři programovacích jazyků si lenost programátorů dobře uvědomují, a tak Ruby umožňuje v definici specifikovat implicitní hodnoty:
irb(main):007:0> def odmocnina(cislo, exponent=2) irb(main):008:1> return cislo**(1.0/exponent) irb(main):009:1> end => nil irb(main):010:0> odmocnina(16, 4) => 2.0 irb(main):011:0> odmocnina(16) => 4.0
Parametry s implicitními hodnotami musí být v definici vždy vpravo a
nelze je prokládat, jinak by nebylo možné zajistit správné přiřazení.
Definice def pitomost(x=4, y, z=2)
je tedy nesmyslná a
chybná.
Rekurze je možnost volat metodu z ní samotné a je silným nástrojem funkcionálních jazyků. V Ruby je nicméně možná také:
irb(main):012:0> def faktorial(n) irb(main):013:1> if n == 0 irb(main):014:2> return 1 irb(main):015:2> else irb(main):016:2* return n * faktorial(n - 1) irb(main):017:2> end irb(main):018:1> end => nil
Algoritmus slouží k výpočtu faktoriálu daného čísla. Je-li hodnota n nenulová, vynásobí se výsledkem volání metody s hodnotou n - 1. Je-li nulová, dojde k navrácení čísla 1 a rozvoj je ukončen. Tímto způsobem tak dosáhneme zcela intuitivního výpočtu, např. 6! = 6 * 5 * 4 * 3 * 2 * 1 = 720. Že to opravdu funguje ověříme snadno:
irb(main):019:0> faktorial(6) => 720
S rekurzí je nicméně třeba nakládat obezřetně, některé výpočty totiž mohou být značně paměťově náročné.
Pro úplnost ještě uvedu, že v definici metod lze vynechat také klíčové
slovo return
a psát tak např. jen:
def odmocnina(cislo) cislo**(1.0/2) end
Osobně však doporučuji se tomuto zkrácení pokud možno vyhnout, k přehlednosti totiž rozhodně nepřispívá.
Než přejdeme k vysvětlení samotné práce s třídami a objekty, bylo by více než vhodné si na tomto místě povědět, co to objektově orientované programování (OOP) vlastně je. Vzhledem k zaměření a rozsahu tohoto seriálu není možné se do detailů věnovat všem jeho aspektům a ani se o to nebudu pokoušet. Existují povolanější a podstatně obsáhlejší zdroje, které se touto problematikou zabývají, a myslíte-li to s programováním vážně, vřele doporučuji si je do budoucnosti prostudovat.
Máte-li už s programováním nějaké předchozí zkušenosti a objektově orientovaný přístup znáte, můžete následující odstavce bez obav přeskočit a přejít rovnou k podkapitole 2.2 Definice tříd.
S rozvojem výpočetní techniky se vyvíjely také programovací jazyky, a to především směrem k větší srozumitelnosti. Současně s nimi se měnily také přístupy ke psaní počítačových programů.
Poměrně dlouhou dobu bylo (a dodnes je) uznávaným modelem tzv. procedurální programování. Program sestává z dat, která jsou uchovávána prostřednictvím proměnných, a procedur, které s těmito daty pracují. Přístup rozdělení jednotlivých ucelených úkonů do samostatných procedur je poměrně efektivní a nepochybně umožnil rozvoj i velmi složitých aplikací (linuxové jádro je celé napsáno v procedurálním jazyce C). Typicky procedurálním jazykem je např. Pascal.
Objektově orientovaný přístup se snaží ještě více přiblížit strukturu programu lidskému myšlení a vnímání reálného světa. Člověk o okolním světě (většinou) neuvažuje jako o sadě proměnných a procedur, ale jako o objektech. Tento přístup umožňuje nejen o problémech přemýšlet a pojímat je jako reálné věci, ale také objekty z jednotlivých objektů skládat, ba dokonce vlastnosti dědit. Díky přístupovým metodám je pak možné ochránit data před nechtěným přepsáním a vnitřní implementace skrýt.
Pokud si některá tvrzení z předchozího odstavce nedokážete představit, pusťte je z hlavy. Vše si názorně ukážeme v následujících kapitolách.
Stejně jako je Aris pes a já jsem člověk, tak i jednotlivé objekty jsou
vždy konkrétními instancemi nějaké třídy. Třída samotná se skládá z
proměnných a metod a její definice je uvozena klíčovým slovem
class
. Konvencí je začínat názvy tříd velkým písmenem:
irb(main):020:0> class Pes irb(main):021:1> def zastekej irb(main):022:2> puts "Haf, haf!" irb(main):023:2> end irb(main):024:1> end => nil
Konkrétní instanci vytvoříme prostřednictvím metody
new
:
irb(main):025:0> aris = Pes.new => #<Pes:0xb7d1308c>
Nyní již můžeme s nově vytvořenou instancí pracovat:
irb(main):026:0> aris.zastekej Haf, haf! => nil
Ponechme prosím stranou absenci jakékoli užitečnosti takovéto třídy. Mějte na paměti, že jde pouze o jednoduchou ukázku na vysvětlení základních principů; postupy zde naučené lze stejně tak dobře aplikovat na komplikované algoritmické úlohy, v rámci přístupnosti a srozumitelnosti se však spokojíme s jednodušší variantou.
Dosud jsme si ukázali, jak definovat metody třídy. K uchovávání hodnot v
rámci jedné instance slouží tzv. proměnné instance a jsou to lokální
proměnné společné všem jejím metodám. Od běžných proměnných se liší tím, že
jejich názvy začínají znakem @
:
rb(main):027:0> class Pes irb(main):028:1> def initialize(jmeno, rasa) irb(main):029:2> @jmeno = jmeno irb(main):030:2> @rasa = rasa irb(main):031:2> end irb(main):032:1> def info irb(main):033:2> return "#{@jmeno} je #{@rasa}." irb(main):034:2> end irb(main):035:1> end => nil
Nadefinovali jsme si dvě proměnné instance (@jmeno
a
@rasa
) a dvě metody (initialize
a
info
).
initialize
je standardní název pro tzv. inicializační
metodu, která umožňuje předání hodnot instanci už při jejím vytvoření.
Na rozdíl od jiných metod se nevolá přímo, ale prostřednictvím již známé
metody new
:
irb(main):036:0> mujPes = Pes.new("Aris", "Border terier") => #<Pes:0xb7ca120c @jmeno="Aris", @rasa="Border terier">
Z návratové hodnoty je patrné, že zadané parametry byly skutečně předány
patřičným proměnným instance. Ověřit si to můžeme také zavoláním
muj_pes.info
.
Možná vás napadlo, že pomocí tečkové notace by se dal vypsat obsah proměnné instance přímo. Když to ale vyzkoušíme, obdržíme následující chybové hlášení:
irb(main):037:0> mujPes.jmeno NoMethodError: undefined method `jmeno' for #<Pes:0xb7c9d6fc @jmeno="Aris" , @rasa="Border terier"> from (irb):37 from :0
O nic úspěšnější nebudeme ani v případě, že zkusíme zadat
mujPes.@jmeno
. Toto chování je dalším aspektem objektového
přístupu – k práci s proměnnými slouží příslušné metody, což nejen
výrazně omezuje možné zavlečení chyby, ale zároveň také skrývá vnitřní
postupy.
Používáme-li například modul Math
, většinou nás nezajímá,
jak je vnitřně implementován. Co potřebujeme vědět je, jaké prostředky nám
poskytuje a jak se s nimi pracuje. Výhodou tohoto přístupu je také to, že
vnitřní implementace jednotlivých metod pak můžeme měnit bez toho, aby byl
jakkoli zasažen kód, který s danou třídou pracuje.
Definice třídy o obohacená o přístupové metody by tak mohla vypadat např. takto:
irb(main):038:0> class Pes irb(main):039:1> def initialize(jmeno, rasa) irb(main):040:2> @jmeno = jmeno irb(main):041:2> @rasa = rasa irb(main):042:2> end irb(main):043:1> def jmeno irb(main):044:2> return @jmeno irb(main):045:2> end irb(main):046:1> def rasa irb(main):047:2> return @rasa irb(main):048:2> end irb(main):049:1> end => nil
V uvedeném příkladu jsou přístupové metody pojmenovány stejně jako
proměnné instance, což umožňuje intuitivní přístup k nim, někteří
programátoři však upřednostňují pojmenování typu vypisJmeno
a
nastavJmeno
. Výběr je opět na vás, nicméně vhodné je zůstat v
rámci programu konzistentní.
Výše uvedená definice má jednu nevýhodu: stále nám neumožňuje do
proměnných zapisovat. Abychom zachovali výše zavedený způsob, nadefinujeme
si metodu pro ukládání do proměnné @jmeno
následujícím
způsobem:
irb(main):050:0> class Pes irb(main):051:1> def initialize(jmeno, rasa) irb(main):052:2> @jmeno = jmeno irb(main):053:2> @rasa = rasa irb(main):054:2> end irb(main):055:1> def jmeno irb(main):056:2> return @jmeno irb(main):057:2> end irb(main):058:1> def rasa irb(main):059:2> return @rasa irb(main):060:2> end irb(main):061:1> def jmeno=(noveJmeno) irb(main):062:2> @jmeno = noveJmeno irb(main):063:2> end irb(main):064:1> end => nil
Nyní můžeme s proměnnou pracovat, jako bychom k ní měli přímý přístup:
irb(main):065:0> mujPes = Pes.new("Aris", "Border terier") => #<Pes:0xb7cd1e0c @rasa="Border terier", @jmeno="Aris"> irb(main):066:0> mujPes.jmeno # puvodni jmeno => "Aris" irb(main):067:0> mujPes.jmeno = "Cassie" # prirazeni => "Cassie" irb(main):068:0> mujPes.jmeno # nove jmeno => "Cassie"
Jak vidíte, autor třídy má nad proměnnými plnou kontrolu a je jen na něm, které z nich a do jaké míry zpřístupní. Tato vlastnost má však jeden nepříjemný důsledek – díky přístupovým metodám se i takto triviální definice protáhla na nepříjemně velký počet řádků. Ruby proto nabízí elegantní zkratku:
irb(main):069:0> class Pes irb(main):070:1> def initialize(jmeno, rasa) irb(main):071:2> @jmeno = jmeno irb(main):072:2> @rasa = rasa irb(main):073:2> end irb(main):074:1> attr_reader :jmeno, :rasa irb(main):075:1> attr_writer :jmeno irb(main):076:1> end => nil
Tento příklad je naprostým ekvivalentem předcházejícího. Příkaz
attr_reader
vytvoří k vybraným proměnným metody pro čtení a
attr_writer
pro zápis. Názvy proměnných přitom začínají
dvojtečkou (nikoli zavináčem!) a jsou od sebe odděleny čárkami.
Už víme, že proměnné instance jsou považovány za soukromé a chceme-li k nim mít přístup „z venčí“, je třeba nadefinovat příslušné metody. To nám umožňuje nejen měnit způsob, jakým je s proměnnými vnitřně nakládáno, ale také rozhodnout, ke kterým proměnným vůbec přístup umožníme.
Ve složitějších třídách často pracujeme s metodami, které jsou volány jinými metodami, zároveň však neočekáváme jejich volání mimo definici třídy. Ruby rozlišuje tři druhy metod:
Pes
, mohou si k těmto metodám navzájem přistupovat. Tyto
metody však nejsou dostupné ani z instancí jiných tříd, ani z jiných
částí programu.Které metody jsou jakého druhu můžeme explicitně stanovit
prostřednictvím klíčových slov public
, protected
a private
:
irb(main):077:0> class Kruh irb(main):078:1> def initialize(polomer) # implicitne verejna irb(main):079:2> @polomer = polomer irb(main):080:2> @PI = 3.14159 irb(main):081:2> end irb(main):082:1> private # nasleduji soukrome irb(main):083:1> def obvod irb(main):084:2> return 2 * @PI * @polomer irb(main):085:2> end irb(main):086:1> def obsah irb(main):087:2> return @PI * @polomer**2 irb(main):088:2> end irb(main):089:1> public # nasleduji verejne irb(main):090:1> def parametry irb(main):091:2> return "polomer:\t" + @polomer.to_s + irb(main):092:2* "\nobvod:\t\t" + obvod.to_s + irb(main):093:2* "\nobsah:\t\t" + obsah.to_s + "\n" irb(main):094:2> end irb(main):095:1> end => nil
Druhou možností je rozlišovat druh metod až v závěru. V našem případě bychom tedy na konec připsali:
public :parametry private :obvod, :obsah
Volba je opět na vás. A že to doopravdy funguje, ověříme snadno:
irb(main):096:0> k = Kruh.new(5) => #<Kruh:0xb7ced378 @PI=3.14159, @polomer=5> irb(main):097:0> puts k.parametry polomer: 5 obvod: 31.4159 obsah: 78.53975 => nil irb(main):098:0> k.obvod NoMethodError: private method `obvod' called for #<Kruh:0xb7ced378 @PI=3.1 4159, @polomer=5> from (irb):98 from :0
Může se stát, že nám možnosti určité třídy přestanou dostačovat. Dědičnost je způsob, jak přejmout metody a proměnné původní třídy a umožnit jejich rozšíření bez nutnosti původní třídu opisovat. Toto je obzvláště výhodné také v případě, že vnitřní implementaci původní třídy neznáme.
Zkusme si nadefinovat novou třídu Stene
, která je v zásadě
speciálním případem třídy Pes
. Štěně je bezpochyby taky pes,
ale na rozdíl od většiny svých slušně vychovaných příbuzných disponuje
jedním nešvarem, a to tím, že rozkouše, na co přijde, obzvláště když mu
rostou zoubky. Definice by potom mohla vypadat nějak takto:
irb(main):099:0> class Stene<Pes irb(main):100:1> def rozkousej(predmet) irb(main):101:2> return "#{predmet} je na cucky!" irb(main):102:2> end irb(main):103:1> end => nil
irb(main):104:0> mujPes = Stene.new("Cassie", "Yorksirsky terier") => #<Stene:0xb7cf91b4 @rasa="Yorksirsky terier", @jmeno="Cassie"> irb(main):105:0> mujPes.jmeno => "Cassie" irb(main):106:0> mujPes.rozkousej("Dalkovy ovladac") => "Dalkovy ovladac je na cucky!"
Budeme-li chtít třídě přidat nové vlastnosti, pravděpodobně budeme muset
zasáhnout také do metody initialize
. Změny chování některé z
děděných metod docílíme její novou definicí, opisovat však celý její obsah
je přinejmenším nepohodlné. Naštěstí lze dědit také obsah přepisované
metody:
irb(main):107:0> class Stene<Pes irb(main):108:1> def initialize(jmeno, rasa, vek) irb(main):109:2> super(jmeno, rasa) irb(main):110:2> @vek = vek irb(main):111:2> end irb(main):112:1> attr_reader :vek irb(main):113:1> attr_writer :vek irb(main):114:1> end => nil
irb(main):115:0> mujPes = Stene.new("Cassie", "Yorksirek", "6 mesicu") => #<Stene:0xb7cd9d8c @rasa="Yorksirek", @jmeno="Cassie", @vek="6 mesicu"> irb(main):116:0> mujPes.jmeno => "Cassie" irb(main):117:0> mujPes.vek => "6 mesicu"
Vedle proměnných instance nabízí Ruby ještě tzv. proměnné
třídy, které jsou společné všem jejím instancím. Jejich názvy začínají
znaky @@
a uvádí se mimo těla definic metod, zpravidla na
začátku definice třídy před metodou initialize
.
V následujícím příkladu si nadefinujeme proměnnou třídy
@@pocet
, která bude uchovávat informace o počtu vytvořených
instancí:
irb(main):118:0> class Pes irb(main):119:1> @@pocet = 0 # promenna tridy irb(main):120:1> def initialize irb(main):121:2> @@pocet += 1 # pripocitame novou instanci irb(main):122:2> end irb(main):123:1> def pocet irb(main):124:2> return @@pocet # informace o aktualnim stavu irb(main):125:2> end irb(main):126:1> end => nil
Snadno ověříme, že to skutečně funguje:
irb(main):127:0> prvniPes = Pes.new => #<Pes:0xb7ceac18> irb(main):128:0> prvniPes.pocet => 1 irb(main):129:0> druhyPes = Pes.new => #<Pes:0xb7ce3cd8> irb(main):130:0> prvniPes.pocet => 2
Nástroje: Tisk bez diskuse
Tiskni
Sdílej:
nejaky velky projekty napsany v RubyZatím nikdo nic extra velkýho neprásknul, ale už jen ve standardní distribuci Ruby by mělo být ~420000 řádků Ruby. To mi přijde celkem dost.
ne nejakej web franty fuky
attr_accessor:
? Potom se do tý proměnný dá i zapisovat a nemusí se na to dělat extra metody stylem:
class Pes def initialize(jmeno) @jmeno = jmeno end def name return #{@jmeno} end def setName(jmeno) @jmeno = jmeno end endMísto toho se použije
class Pes attr_accessor: jmeno def initialize(jmeno) @jmeno = jmeno end end