Ruby po kapkách – úkoly a řešení (2.)

1. dubna 2009

V tomto článku se opět budeme věnovat úkolům, na nichž si můžete otestovat čerstvě nastudované možnosti jazyka Ruby. Začněme řešením minulého zadání.

V prvním úkolu jsme předpokládali, že uživatel zadá na STDIN buď slovo „ano“ nebo slovo „ne“ a stiskne enter (pro zjednodušení nebylo třeba ošetřovat žádné jiné stavy ani potenciální chyby). Cílem bylo dopsat chybějící první část následujícího kódu a najít řešení, kde by se nepoužila podmínka. Volitelnou součástí bylo doplnit situaci, kdy není zadáno ani jedno ze zmíněných slov.


if p
puts „Bylo to ANO!“
else
puts „Bylo to NE!“
end

Když se na fragment kódu podíváme, můžeme problém přeformulovat. Potřebujeme dosáhnout toho, aby na základě nějaké hodnoty načtené ze vstupu byla v proměnné p hodnota true nebo false. Respektive aby to byla hodnota, která se vyhodnotí jako pravda či nepravda v podmínce. Pro tento účel nám vyhovuje již osvědčená metoda index.

p = gets.index(‚ano‘)
if p
puts „Bylo to ANO!“
else
puts „Bylo to NE!“
end

V případě, že návratová hodnota gets obsahuje slovo „ano“, bude do p uložena 0 jako index znaku, na kterém začíná hledaný řetězec. 0 se vyhodnocuje v podmínce jako true. Podle zadání byl jediný jiný možný vstup slovo „ne“. V takovém případě vrátí index nil, který se vyhodnotí v podmínce jako nepravda. Podívejme se ještě na jiné možné řešení, které je obecnější a rozeznává i vstup, který neodpovídá ani jednomu známému slovu.

t = Hash.new(‚neznámé slovo‘)
t[‚ano‘] = ‚ANO‘
t[‚ne‘] = ‚NE‘
x = gets.chop
puts „Bylo to #{t[x]}!“

Tento program využívá hash jako překladovou tabulku. Defaultní hodnota hashe ošetří všechna nedefinovaná slova. Povšimněte si ještě volání metody chop na výstup z gets. Tato metoda totiž načítá celý řádek vstupu včetně znaku (nebo znaků) označujícího konec řádku. Metoda chop tento koncový znak z řetězce odstraní.

Druhým úkolem bylo vytvořit filtr, který by načítal slovník a vypisovat palindromy – slova, která jsou stejná, i když je čteme odzadu. Slovník byl definován jako textový soubor, kde je na každém řádku jedno slovo. Řešení je velmi jednoduché.

while line = gets.chop
puts line if line == line.reverse
end

Pro správnou funkci je třeba opět použít chop. Poslední úkol byl malinko těžší, ale s rozpomenutím se na školní matematiku by mělo být možné ho s dosavadními znalostmi Ruby vyřešit. Zadáním bylo napsat program, který vypíše všechna prvočísla menší než 100. Jedno možné řešení je ukázáno níže.

2.upto(100) do |n|
p = 2
q = (n ** 0.5).to_i + 1
while p != q
break if n % p == 0
p += 1
end
puts n if p == q
end

Víme, že prvočísla jsou větší než jedna a shora máme omezení také zadáno. Vnější smyčka programu tedy postupně do proměnné n načítá čísla od 2 do 100. Vnitřní smyčka pak zkouší zbytek po dělení až do druhé odmocniny a předčasně skončí, pokud nalezne dělitele, kterým je aktuální n dělitelné beze zbytku. Nejvyšší vyzkoušený dělitel je uchován v proměnné p. Pokud se na konci vnitřního cyklu rovná nejvyšší vyzkoušený dělitel druhé odmocnině testovaného čísla, je číslo vypsáno jako prvočíslo. Získání druhé odmocniny jsme zatím neuváděli. Pomohli jsem si proto umocňovacím operátorem a mocněním na jednu polovinu. Aby algoritmus fungoval v mezním případě (ponecháme na čtenáři, aby si uvědomil, který to je), je k druhé odmocnině převedené na celé číslo ještě přičtena jednička.

Za zmínku stojí ještě zápis bloku předaného metodě upto. Protože je blok dlouhý několik řádků, velí konvence uzavřít ho mezi klíčová slova do a end namísto složených závorek. Oba zápisy jsou však jinak ekvivalentní.

Prvním dnešním zadáním úkolu bude přepsat příklad s prvočísly za pomocí čerstvých znalostí metod, které jsou dostupné pro všechny Enumerable objekty. K tomu si ukážeme ještě dvě navíc, které umožňují testovat, zda zadaný blok vrací hodnotu true pro všechny nebo aspoň pro jeden z prvků kolekce. (K řešení nejsou tyto metody nutné, ale mohou se hodit.)

irb(main):001:0> [1, 2, 3].all? { |e| e > 1 }
=> false
irb(main):002:0> [1, 2, 3].any? { |e| e > 1 }
=> true

Funkcionalitu těchto metod by bylo možné implementovat pomocí metody inject. To je zároveň zadání druhého úkolu. Pokuste se také přijít na to, jaké omezení má implementace pomocí inject oproti nativní implementaci. Napovědět by vám měl následující kousek kódu.

irb(main):003:0> [1, 2, 3].any? { |e| puts e > 1; e }
false
=> true

Poslední dnešní úloha spočívá v transformaci pole. Předpokládejme, že máme vstupní pole, které obsahuje prvky v podobě celých čísel od 0 do 9. Pole může vypadat například takto: [1, 3, 1, 5, 5, 8, 2, 9]. Vytvořte kód, který toto pole převede na pole, kde pro každý index od 0 do 9 je hodnotou, kolikrát se tento index objevil jako hodnota v prvním poli. Pro náš příklad bude tedy výsledek: [0, 2, 1, 1, 0, 2, 0, 0, 1, 1] (to znamená, že 0 se v prvním poli neobjevila ani jednou, 1 dvakrát, 2 jedenkrát atd.). Snažte se nalézt krátké, ale čitelné řešení.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Štítky: Články

Mohlo by vás také zajímat

Nejnovější

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *