Naloga

Sestavite program, ki uporabnika vpraša za niz ter razcepi ta niz v seznam besed in ga izpiše.

Besede so ločene z enim ali več presledki; tudi če je presledkov več, naj se program vede, kot da je en sam.

Primer:

Vnesi niz: Tole je primer za domaco nalogo ['Tole', 'je', 'primer', 'za', 'domaco', 'nalogo']

Za branje uporabnikovega vhoda namesto input uporabite raw_input. Program, ki razcepi niz, morate napisati sami; uporaba metod, kot je split, je povsem prepovedana.

Zelo zasilna rešitev domače naloge

Preprosta, a zasilna in ne povsem pravilno delujoča rešitev naloge je takšna:

stavek = "Tole je primer za domaco nalogo" besede = [] beseda = "" for crka in stavek: if crka != " ": beseda += crka else: besede.append(beseda) beseda = "" besede.append(beseda) print(besede)

Logika programa je takšna: v spremenljivko beseda dodajamo črke, dokler ne naletimo na presledek. Ko se to zgodi, dodamo besedo beseda v seznam besed (besede.append(beseda)) in začnemo znova, s prazno besedo (beseda = ""). Ko je stavka konec, torej, ko se zanka for izteče, dodamo na seznam besed še zadnjo besedo, tisto, ki ji ni sledil presledek in je torej nismo dodali.

Predpriprava pred vsem skupaj, še pred zanko, je, da pripravimo prazen seznam besed (da bomo imeli v zanki kam dodajati nove besede) in da pripravimo prazno besedo (da bomo imeli kam dodajati črke).

Muke smo si nekoliko skrajšali s tem, da smo se znebili inputa in določili kar fiksen niz. To je dobro za čas, dokler programiramo; kasneje naj bi to zamenjali z input, kot zahteva naloga. Splošni nauk: ko programiraš, si poenostavljaj delo s testiranjem programa. Večina časa pri programiranju, kot ste že odkrili, ne gre v pisanje temveč v odkrivanje, zakaj in kako program ne deluje (delanje napak zahteva veliko manj časa kot popravljanje). To ni nič ilegalnega, če končno različico programa popraviš, da je takšna, kot mora biti.

Pravi razlog, da program imenujem zasilen, je, da ne upošteva pravilno dvojnih presledkov. Izpiše namreč tole.

['Tole', 'je', 'primer', '', '', '', '', 'za', 'domaco', 'nalogo', '', ''] Podobno narobe bi deloval, če bi na konec stavka dodali še dva presledka. Tudi tadva bi se znašla v seznamu.

Rešitev je preprosta. (Se mi tale stavek morda prepogosto zapiše? Če, potem zato, ker je pač vedno tako. Vse prave rešitve so preproste. Skoraj.) Če je beseda prazna, je ne dodamo.

stavek = "Tole je primer za domaco nalogo " besede = [] beseda = "" for crka in stavek: if crka != " ": beseda += crka else: if beseda: besede.append(beseda) beseda = "" if beseda: besede.append(beseda) print(besede)

Pazite, kako smo naredili if v zanki, takole:

if beseda: besede.append(beseda) beseda = ""

Pogoj "prepreči" le dodajanje praznih besed, ne pa tega, kar sicer naredi presledek, namreč "resetiranja" besede. Pravzaprav je tule v resnici vseeno. Lahko bi pisali tudi

if beseda: besede.append(beseda) beseda = ""

Pravzaprav bi bilo celo bolj ekonomično: če vemo, da je beseda, prazna, je nima smisla ponovno nastavljati na prazno.

Če koga moti, da na koncu programa posebej obravnavamo še zadnjo besedo, lahko postavi stražarja, presledek na koncu stavka.

stavek = "Tole je primer za domaco nalogo" besede = [] beseda = "" for crka in stavek+" ": if crka != " ": beseda += crka else: if beseda: besede.append(beseda) beseda = "" print(besede)

Sprememba - poleg tega, da smo izbrisali šaro na koncu - je tako majhna, da je na prvi pogled skoraj ne opazimo: v glavi zanke for smo stavek spremenili v stavek+" ". Tako smo dosegli, da bo tudi zadnja beseda (ki s tem nekako ni več zadnja), obravnavana znotraj zanke.

Za opombo pod črto povejmo, da je pogosto boljše, če se takim rešitvam izogibamo. Nepregledne so, poleg tega pa je lahko prištevanje presledka "drago", if za zanko pa poceni. Sploh pa v našem primeru, kjer je tisto znotraj ifa čisto kratko; če bi tam namesto appenda ponovili deset vrstic kode, ki se sicer nahaja v zanki, nič ne rečem. Kaj je drago in kaj poceni, pa je odvisno od situacije in jezika, v katerem delamo; to boste spoznavali sproti, ko boste boljši in boljši programerji.

Nekoliko nenavadna rešitev z dvema zankama

Naša rešitev domače naloge sicer rešuje nek konkreten problem, vzorček, po katerem teče rešitev, pa je kar pogost. Spoznajmo še enega. Domače naloge v praksi nikoli ne bi reševali tako, kot opisujemo spodaj, sam vzorec pa nam pride kdaj prav.

stavek = "Tole je primer za domaco nalogo" besede = [] i = 0 while i < len(stavek): while i < len(stavek) and stavek[i] == " ": i += 1 beseda = "" while i < len(stavek) and stavek[i] != " ": beseda += stavek[i] i += 1 if beseda: besede.append(beseda) print(besede)

Namesto zanke for smo tokrat uporabili while. Imeli bomo števec, i, s katerim bomo "korakali" prek zanke in opazovali i-ti znak, stavek[i]. Znotraj zanke pa imamo dve zanki. Prva preskoči vse presledke, v bistvu naredi tole:

while stavek[i] == " ": i += 1 Dodali smo le še, da se sme to dogajati le, dokler ne pridemo do konca, torej i < len(stavek). (Če koga mika reči, da za to skrbi že zunanja zanka, naj pomisli še enkrat: dokler se program vrti v notranji zanki, ne preverja pogoja iz zunanje; tisti pride na vrsto šele, ko se mora Python odločiti, ali bo še enkrat ponovil "ta veliko", zunanjo zanko.) Vrstni red pogojev je pomemben. Če bi namesto while i < len(stavek) and stavek[i] == " ": i += 1 napisali while stavek[i] == " " and i < len(stavek): i += 1 ne bi bilo dobro, saj moramo to, ali je i prevelik, preveriti, še preden preverjamo, kakšen je i-ti znak.

Ko se odvrti prva notranja zanka, smo lahko prepričani, da je i bodisi prevelik, bodisi je i-ti znak nekaj, kar ni presledek. Zdaj naredimo nekaj obratnega kot zgoraj: i povečujemo, dokler i-ti znak ni presledek, in pri tem dodajamo i-ti znak v beseda. Ko se zanka odvrti, smo prišli bodisi na konec stavka (ker ne drži več i < len(stavek) bodisi do novega presledka (ker ne drži stavek[i] != " ". V vsakem primeru pogledamo, ali se je v beseda nabrala kaka beseda in jo v tem primeru dodamo.

Program priporočam v meditacijo.

Klasična rešitev

V Pythonu smo zelo komot, ko gre za seštevanje nizov. V nekaterih jezikih se temu raje izogibamo in tam bi bila običajnejša (za nas, ki delamo v Pythonu pa pač le poučnejša) nekoliko drugačna rešitev.

stavek = "Tole je primer za domaco nalogo" besede = [] zadnji_presl = -1 i = 0 while i <= len(stavek): if i == len(stavek) or stavek[i] == " ": if i != zadnji_presl+1: besede.append(stavek[zadnji_presl+1:i]) zadnji_presl = i i += 1 print(besede)

Logika tega programa je takšna: program si zapomni, kdaj je videl zadnji presledek. Z zanko while gre s števcem i, tako kot prej, prek stavka. Če je i-ti znak presledek, ne naredi prav nič - le i poveča. Če pa gre za presledek, pogleda, ali je bil zadnji videni presledek ravno prejšnja črka - v tem primeru bi držalo i == zadnji_presl+1. Vprašamo se torej if i != zadnji_presl+1: in v tem primeru dodamo vse od zadnjega presledka, do tega presledka - kar je, seveda, ravno zadnja beseda. Konkretno, dodati moramo stavek[zadnji_presl+1:i]; napisali smo zadnji_presl+1, da vzamemo prvi znak za zadnji presledkom. Gornja meja pa je i, saj rezine, kot se spomnimo s prejšnjega tedna, ne vsebujejo gornje meje - rezanje samo bo poskrbelo, da dobimo vse znake do (vključno) i minus ena, ne do i.

Posebna sitnost je zadnja beseda; prvotna različica tega programa jo je izpustila in na napako so morali opozoriti študenti. Zaradi nje gremo z zanko do enega elementa predaleč (i <= len(stavek)) namesto (i < len(stavek)) in potem v if preverimo, ali smo morda na "po-zadnjem" znaku, ki ga obravnavamo kot presledek. Seveda bi lahko isto dosegli tudi z dodajanjem besede na koncu, za zanko; v tem primeru bi morali ponoviti naslednji if iz zanke.

Še zadnja sprememba: zanko while zamenjamo s for.

stavek = "Tole je primer za domaco nalogo" zadnji_presl = -1 besede = [] for i in range(len(stavek) + 1): if i == len(stavek) or stavek[i] == " ": if i != zadnji_presl+1: besede.append(stavek[zadnji_presl+1:i]) zadnji_presl = i print(besede)

Vedno, kadar while ne počne drugega, kot da šteje, se pravi, vedno, kadar je videti takole

stevec = zacetek while stevec < konec: (...) stevec += 1 in se ne stevec ne konec znotraj zanke ne spreminjata, jo lahko zamenjamo z for i in range(zacetek, konec): (...) pa se s tem izognemo nastavljanju spremenljivke stevec pred zanko in njenemu povečevanju znotraj zanke. Če je zacetek enak 0, ga lahko tudi izpustimo. for i in range(konec): (...)

Dober nasvet

Polagam na srce: oglejte si vse, kar smo sprogramirali tule - predvsem tole, zadnjo rešitev - in ko boste prepričani, da ga razumete, odložite vse skupaj, pojdite na sprehod ali pa se eno uro učite Diskretne strukture. Nato se vrnite za računalnik in program (brez škiljenja!) napišite ponovno, sami. Preverite rešitev in ponavljajte vse skupaj, dokler vam ne uspe.

Last modified: Thursday, 11 February 2016, 2:39 PM