Vodni top s funkcijami
Domača naloga se navezuje na prejšnjo domačo nalogo, vodni top. Pri reševanju smete uporabiti dele programa, ki ste ga pisali prejšnji teden, ali pa celo dele rešitve, ki je objavljena na spletu. Bistvo naloge je namreč v tem, da se naučimo zapakirati tisto, kar smo delali prejšnji teden, v lepe in pregledne funkcije.
Testi
testi-vodni-top-s-funkcijami.py
Ogrevalne funkcije
koordinate(ime, kraji)
prejmeime
kraja in seznam krajev (kraji
), kot smo ga vajeni iz prejšnje domače naloge. Funkcija vrne terko s koordinatama podanega kraja. Če tega kraja ni, naj ne vrne ničesar (torejNone
).razdalja_koordinat(x1, y1, x2, y2)
dobi koordinate dveh točk in vrne razdaljo med njima.razdalja(ime1, ime2, kraji)
prejme imeni dveh krajev in vrne razdaljo med njima.
Funkcije se morajo lepo in zgledno klicati med sabo. Funkcija razdalja
mora pridobiti koordinate in izračunati razdaljo tako, da pametno uporablja prvi dve funkciji.
Rešitev
Naloga, modro, najprej zahteva funkcijo koordinate
, s katero dobimo koordinate podanega kraja. Takšno funkcijo je modro napisati, ker nam bo pomagala pri vseh ostalih.
Preprosta je: z zanko gremo čez kraje, dokler ne naletimo na tistega s pravim imenom in tedaj vrnemo njegove koordinate.
def koordinate(ime, kraji):
for kraj, x, y in kraji:
if kraj == ime:
return x, y
En način, kako to narediti neposrečeno, je zaplesti zanko takole:
for podatki in kraji:
kraj, x, y = podatki
Prepričan sem, da je marsikateri na ušesih sedeči študent (ali takšen, ki že vse zna :) napisal
for podatki in kraji:
kraj = podatki[0]
x = podatki[1]
y = podatki[2]
Druga nepotrebna komplikacija je tole
def koordinate(ime, kraji):
for kraj, x, y in kraji:
if kraj == ime:
break
return x, y
Preprosteje je, če return
napišemo kar v zanki, saj s tem tako ali tako prekinemo zanko in vrnemo rezultat. Še bolj nepotrebna komplikacija je
def koordinate(ime, kraji):
for kraj, x, y in kraji:
if kraj == ime:
iskani_x, iskani_y = x, y
return iskani_x, iskani_y
Ko enkrat najdemo, vrnemo.
Naslednja pomožna funkcija je razdalja_koordinat
. Tu le vrnemo, kar nam naračuna stari Grk.
from math import *
def razdalja_koordinat(x1, y1, x2, y2):
return sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
Funkcija razdalja
zdaj dvakrat pokliče funkcijo koordinate
, da izve koordinate prvega in drugega kraja, nato pa funkcijo razdalja_koordinat
, da ta izračuna razdaljo.
def razdalja(ime1, ime2, kraji):
x1, y1 = koordinate(ime1, kraji)
x2, y2 = koordinate(ime2, kraji)
return razdalja_koordinat(x1, y1, x2, y2)
Testi vam ne bodo pustili, da bi funkcijo napisali takole:
def razdalja(ime1, ime2, kraji):
for kraj, x1, y1 in kraji:
if kraj == ime1:
break
for kraj, x2, y2 in kraji:
if kraj == ime2:
break
return sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
Ideja domače naloge je, da se naučimo delati s funkcijami. Ne le sestavljati, temveč tudi uporabljati lastne funkcije. V tem pogledu ta rešitev ni pravilna, pa čeprav bi sicer dala pravilni rezultat.
Obvezni del
v_dometu(ime, domet, kraji)
vrne seznam krajev, ki jih lahko zalije krajime
, če ima top z dometomdomet
. Kraj ne zaliva sebe.najbolj_oddaljeni(ime, imena, kraji)
prejmeime
nekega kraja, seznam imen nekih krajev (imena
) in že običajni seznam terk z imeni in koordinatami krajev. Med kraji v seznamuimena
(ne med vsemi kraji, temveč samo med temi!) mora vrniti ime tistega, ki je najbolj oddaljen od krajaime
. Če recimo, pokličemonajbolj_oddaljeni("Ljubljana", ["Domžale", "Kranj", "Maribor", "Vrhnika"], kraji)
, kjer sokraji
vsi kraji iz prejšnje naloge, vrne"Maribor"
, saj je Maribor med temi štirimi kraji najdalj od Ljubljanezalijemo(ime, domet, kraji)
vrne ime najbolj oddaljenega kraja, ki ga lahko zalije krajime
, če ima top z dometomdomet
.
Rešitev
Tole je malo podobno kot prej: dve funkciji, tretja pa le pokliče tidve.
Funkcija v_dometu
pripravi prazen seznam. Nato gre čez vse kraje; preveri ali je razdalja večja od 0 (in torej ne gre za isti kraj) in manjša od dometa; če to drži, ga doda na seznam. Ta seznam potem na koncu vrne.
def v_dometu(ime, domet, kraji):
zaliti = []
for kraj, x, y in kraji:
if 0 < razdalja(ime, kraj, kraji) <= domet:
zaliti.append(kraj)
return zaliti
Funkcija najbolj_oddaljeni
počne nekaj, kar smo že velikokrat počeli -- recimo takrat, ko smo iskali ime najtežje osebe v seznamu. Iščemo največjo reč po nekem kriteriju; tokrat torej najbolj oddaljeni kraj iz seznama nekih krajev.
def najbolj_oddaljeni(ime, imena, kraji):
naj_razdalja = 0
for kraj in imena:
r = razdalja(ime, kraj, kraji)
if r > naj_razdalja:
naj_razdalja = r
naj_kraj = kraj
return naj_kraj
Zadnja funkcija je potem trivialna: vrniti moramo najbolj oddaljeni kraj izmed krajev, ki so v dometu. Napišemo lahko
def zalijemo(ime, domet, kraji):
kandidati = v_dometu(ime, domet, kraji)
return najbolj_oddaljeni(ime, kandidati, kraji)
ali pa kar
def zalijemo(ime, domet, kraji):
return najbolj_oddaljeni(ime, v_dometu(ime, domet, kraji), kraji)
Dodatni del
presek(s1, s2)
prejme dva seznama in vrne seznam elementov, ki se pojavijo v obeh. Vrstni red elementov v vrnjenem seznamu je lahko poljuben.skupno_zalivanje(ime1, ime2, domet, kraji)
prejme dve imeni krajev, domet vodnih topov, ki ju imajo v teh dveh krajih, in seznam vseh krajev. Vrniti mora seznam vseh krajev, ki jih lahko zalivamo iz obeh krajev.
Rešitev
Dodatna naloga je tokrat izjemoma preprostejša od obveznih. :)
Da izračunamo presek, najprej pripravimo prazen seznam. Nato gremo prek enega seznama, za vsak njegov element pogledamo, ali ga najdemo tudi v drugem seznamu in ga, če je tako, dodamo v oni seznam, v katerem zbiramo presek.
def presek(s1, s2):
p = []
for e in s1:
if e in s2:
p.append(e)
return p
Enkrat kmalu se bomo učili, da se da v Pythonu to narediti tudi veliko preprosteje in hitrejše.
Skupno zaliti kraji so potem tisti kraji, ki so v preseku krajev, ki so v dometu.
def skupno_zalivanje(ime1, ime2, domet, kraji):
return presek(v_dometu(ime1, domet, kraji),
v_dometu(ime2, domet, kraji))