Examining Items #2
Goals
- spájanie zoznamov
- ďalšie výhody využitia príkazu
for-else
Content
Za domácu úlohu ste mali rozšíriť príkaz
preskumaj
o možnosť opísať nie len predmet, ktorý sa nachádza v inventári, ale aj v miestnosti. Prejdeme si možné riešenia spolu.Najjednoduchšie riešenie spočíva v tom, že skopírujem fragment realizujúci opis predmetu v batohu a zmením len zoznam predmetov, ktorý je v tomto prípade inventárom na zoznam predmetov v aktuálnej miestnosti:
for item in inventory: if item["name"] == cmd[1]: print(item["description"]) break else: print("Taký predmet tu nikde nevidím.") for item in world[location]["items"]: if item["name"] == cmd[1]: print(item["description"]) break else: print("Taký predmet tu nikde nevidím.")
Ak však tento fragment spustíme, problém, s ktorým tu budeme zápasiť je viacnásobný výpis textu:
Taký predmet tu nikde nevidím.
Text sa vypíše vtedy, ak sa predmet:
- nachádza v batohu, ale nenachádza sa v miestnosti,
- nachádza v miestnosti, ale nenachádza sa v batohu, alebo
- nenachádza ani v batohu ani v miestnosti (v tomto prípade sa vypíše 2x).
Za tento problém môže to, že oba cykly sa spustia vždy. A teda v prípade, že sa predmet nenájde v batohu, uvedená správa sa vypíše. A podobne, ak sa predmet nenájde v miestnosti, opäť sa vypíše.
Aby sme tomu predišli, vytvoríme si pomocnú premennú
found_in_inventory
, ktorá bude fungovať ako príznak toho, či sme predmet v inventári našli alebo nie. Nastavíme ju na počiatočnú hodnotuFalse
a na hodnotuTrue
ju nastavíme vtedy, keď predmet nájdeme v inventári. Následne druhý cyklus spustíme len vtedy, ak sme predmet v inventári nenašli (a teda hodnota premennejfound_in_inventory
jeFalse
):found_in_inventory = False for item in inventory: if item["name"] == cmd[1]: print(item["description"]) found_in_inventory = True break else: print("Taký predmet tu nikde nevidím.") if found_in_inventory == False: for item in world[location]["items"]: if item["name"] == cmd[1]: print(item["description"]) break else: print("Taký predmet tu nikde nevidím.")
Ak však spustíme aktuálnu úpravu, všetko bude fungovať len v prípade, ak predmet nájdeme v inventári (nevypíše sa žiadna ďalšia správa). Ak však predmet v inventári nenájdeme, sme na tom rovnako. Problémom je v tomto prípade časť
else
v prvom cykle, ktorá sa spustí zakaždým, keď predmet nenájdeme v inventári a teda správu o neúspechu zobrazí. Riešením bude odstránenie tejto časti. Upravený kód bude vyzerať nasledovne:found_in_inventory = False for item in inventory: if item["name"] == cmd[1]: print(item["description"]) found_in_inventory = True break if found_in_inventory == False: for item in world[location]["items"]: if item["name"] == cmd[1]: print(item["description"]) break else: print("Taký predmet tu nikde nevidím.")
Tentokrát už kód bude fungovať ako má. Takéto riešenie je celkom časté pre jazyky založené na jazyku C (ako Java, C# a podobne). S využitím vlastností, ktoré ponúka jazyk Python však tento kód môžeme ešte ďalej upravovať a teda aj zjednodušovať. Využiť na to môžeme práve cyklus
for-else
a časťelse
, do ktorej presuniemie celé prehľadávanie v zozname predmetov v miestnosti. Nepotrebujeme teda žiadnu premennú navyše.for item in inventory: if item["name"] == cmd[1]: print(item["description"]) break else: for item in world[location]["items"]: if item["name"] == cmd[1]: print(item["description"]) break else: print("Taký predmet tu nikde nevidím.")
Okrem toho môžeme využiť ďalšiu vlastnosť, ktoru je spájanie zoznamo dokopy. To si môžeme ilustrovať na nasledovnom fragmente:
l1 = ['jano', 'fero', 'jozo'] l2 = [1, 2, 3, 4] l1 + l2 ['jano', 'fero', 'jozo', 1, 2, 3, 4]
Vďaka tejto vlastnosti môžeme našu aktuálnu implementáciu upraviť naozaj elegantne a vo výsledku nebudeme mať počet riadkov nijak väčší ako ten, ktorý sme už mali na začiatku:
for item in inventory + world[location]["items"]: if item["name"] == cmd[1]: print(item["description"]) break else: print("Taký predmet tu nikde nevidím.")
Upravte implementáciu tak, že fragment kódu, ktorý definuje správanie príkazu
preskumaj
osamostatníte do samostatnej funkcie s názvomexamine_item()
.Najprv identifikujme parametre, ktoré táto funkcia potrebuje:
room
- miestnosť, v ktorej chceme preskúmavať príkazy (vždy aktuálna)inventory
- hráčov inventárline
- príkazový riadok, z ktorého vystrihneme názov predmetu, ktorý chce hráč preskúmať alebo nič, ak hráč zadal len samotný príkazpreskumaj
Následne vytvoríme samostatnú funkciu, prekopírujeme do nej príslušný kód a jemne ho refaktorujeme:
def examine_item(room, inventory, line): cmd = line.split(maxsplit=1) if len(cmd) == 1: print("Ta ja neznám čo chceš skúmať.") else: for item in inventory + room["items"]: if item['name'] == cmd[1]: print(item['description']) break else: print("Taký predmet tu nikde nevidím.")
A túto funkciu zavoláme z hlavnej slučky, keď rozpoznáme, že sa jedná o príkaz
preskumaj
:elif line.startswith("preskumaj"): examine_item(world[location], inventory, line)
Homework
Počas sviatkov sa zaregistrujte na portáli www.codewars.com a vyriešte niekoľko kata v jazyku Python.