Giska á töluna
Giskaðu á töluna!
Þetta verkefni gengur út á að skrifa stutt forrit sem ákveður tölu af handahófi innan fyrirfram ákveðinna marka. Notandinn reynir að giska á töluna og fær til baka svar um það hvort talan sé hærri eða lægri. Þegar hann giskar á rétta tölu eru skilaboð prentuð og keyrslu lýkur.
Í þessu verkefni koma fyrir eftirfarandi þættir:
forritseiningar (e. modules)
heiltölur
if, elif og else skilyrðingar
innbyggð föll
strengir
tagskipti (e. type conversion)
tvíundabreytur (e. booleans)
while-lykkja
Hverju stefnum við að?
Virkni einfaldrar útgáfu þessa forrits gæti litið svona út:
Síðar gætum við bætt fleiri eiginleikum við forritið eins og til dæmis teljara sem segir í lokinn hversu margar tilraunir þurfti til að finna töluna. Það er hægt að bæti ýmsum eiginleikum við þetta forrit en í sinni einföldustu útgáfu koma þó ýmsir grundvallar þættir fyrir. Það er þess vegna sem þetta forrit er einnig vinsælt sem fyrsta forrit hjá þeim sem hyggjast læra nýtt forritunarmál.
Hvar eigum við að byrja?
Hvernig látum við tölvuna finna tölu af handahófi, án þess að við vitum fyrir fram hver hún er? Python er ekki með innbyggt slembifall sem skilar okkur tölu af handahófi en við getum hins vegar nálgast það úr fyrirbæri sem kallast staðlað forritasafn (e. Standard Library) og fylgir alltaf með Python. Við þurfum hins vegar að flytja inn (e. import) þetta slembifall og það er hluti af forritseiningu sem heitir random. Meira um forritseiningar og Python Standard Library í kaflanum um forritseiningar Til að nálgast slembifallið þurfum við bæta inn í forritið okkar innflutnings setningu (e. import statemend) sem verður áfram efst í forritinu.
Því næst skulum við búa til breytu fyrir slembitöluna okkar. Við reynum að velja lýsandi heiti og köllum hana leynitölu. Fallið sem við notum heitir randint() (random integer) og skilar, eins og nafnið gefur til kynna, einungis heiltölum. Annað sem við þurfum að hafa í huga er að þegar við köllum á fallið þurfum við að tilgreina lægstu mögulegu töluna og hæstu mögulegu töluna. Í þessu dæmi skulum við hafa spanið frá 1 og upp í 100.
Við hefðum auðvitað getað fært tölurnar undir breytur og notað þær sem gildi þegar við köllum á slembifallið. Það getur verið mjög þægilegt ef við þurfum að nota tölurnar aftur seinna.
Látum þetta gerast aftur og aftur ...
Næst þurfum við að láta forritið keyra „endalaust“ þar til ákveðnum skilyrðum hefur verið mætt. Til að gera það munum við nota while-lykkju. Til að átta okkur betur á hvernig forritið mun virka þá búum við til breytu sem hefur ákveðið gildi og segjum while-lykkjunni að halda áfram á meðan þessi breyta heldur því gildi. Við búum því til tvíundabreytu (Boole), köllum hana leikur_i_gangi og veitum henni gildið True
.
Við skulum næst búa til breytu fyrir ílag (e. input) notandans en það er talan sem hann giskar á. Til að taka við ílagi frá notanda notum við fall sem heitir input() (meira um það í kafla um innbyggð föll).
Við þurfum að hafa í huga að skilagildi input()-fallsins er strengur og skilagildi fallsins randint() er heiltala. Við getum ekki borið saman ólík tög eins og streng og heiltölu og slíkur samanburður skilar villu sem Python kallar TypeError.
Til þess að fá ekki slíka villu þurfum við annað hvort að breyta gisk
í heiltölu eða leynitala
í streng. Í þessu forriti ætlum við að breyta gisk í heiltölu.
Nú þegar við getum látið forritið keyra aftur og aftur (... og aftur) og við getum tekið við ágiskun notandans þurfum við að fara að huga að skilyrtum setningum (e. conditional statements) eins og if, elif og else. Tilgangur þeirra í þessu forriti verður að bregðast við ílaginu frá notandanum og að ákveða hvað skuli gera í framhaldinu. Við byrjum á að ákveða hvað gerist ef notandinn slær inn rétta tölu. Til þess notum við if setningu þar sem við skoðum hvort að breytan gisk
sé sú sama og leynitala
og bregðumst síðan við með skilaboðum til notandans ef svo reynist vera.
Notum tækifærið hér og skoðum hvernig forritið okkar er farið að líta út.
Eins og forritið okkar lítur út mun það keyra aftur og aftur og ef við giskum á rétta tölu fáum við skilaboðin Jei, þú giskaðir á rétta tölu!. Vandamálið er að það er ekkert sem segir forritinu að hætta að keyra. Þetta kallast endalaus lykkja. Við þurfum því að bæta við if bálkinn (það sem er undir if setningunni) gildisveitingu fyrir breytuna leikur_i_gangi
þannig að gildi hennar verði False
. Rifjum upp að þegar við gerðum while lykkjuna þá sögðum við henni að halda áfram á meðan leikur_i_gangi
væri True
. Þetta þýðir að ef leikur_i_gangi
fær gildið False
þá eru forsendur while
lykkjunnar brostnar og hún hættir að keyra.
Núna erum við komin með forrit sem tekur við ílagi frá notanda þar til hann giskar á rétta tölu og rýfur þá keyrslu. Forritið gæti þá litið svona út:
Til að gera notandanum aðeins auðveldara fyrir skulum við bæti við vísbendingum þegar hann giskar á ranga tölu. Vísbendingin gæti sagt til um hvort talan sé hærri eða lægri en sú sem notandinn giskaði á.
Til að gera þetta getum við bætt elif setningum í bálkinn undir while-lykkjunni, á eftir if setingunni. Þessar elif setinginar eru alltaf notaðar á eftir if setningum en eru annars eins í notkun. Í staðinn fyrir að athuga hvort breytan gisk
sé eins og leynitala
, sem við gerðum með ==
virkjanum ætlum við að kanna hvort hún sé (mögulega) stærri eða minni. Það gerum við með virkjum sem kallast vinstri- og hægri fleygar (<
og >
) en margar kannast líklega við þessa virkja úr stærðfræði (í kaflanum um gagnatög er meira fjallað um samanburðarvirkja).
Lykkjan okkar gæti þá litið svona út:
Á þessum tímapunkti erum við komin með forrit sem getur tekið við ílagi frá notanda, sagt til um hvort að „ágiskunin“ sé rétt, of há eða of lág. Sé ágiskun rétt hættir forritið að keyra.
Í fyrsta sýnidæminu, þar sem við sáum virknina sem við vildum stefna að, komu fyrir tveir þættir sem við munum núna bæta inn í forritið; teljari og nafn leikmanns (notanda). Til að fá nafn leikmannsins notum við sömu aðferð og við notuðum til að taka við ágiskunum, input() fallið. Við viljum þó ekki spyrja um nafnið í hvert skipti sem leikmaðurinn giskar á tölu. Þess vegna biðjum við um nafnið á undan while lykkjunni og þannig verður einungis beðið um nafn í upphafi.
Hvað varðar teljarann, þá búum við breytu sem fær heiltölugildið 0 (núll).
Til að gildi breytunnar teljari
hækki (og „telji“) þurfum við að bæta setningu efst í bálkinn þar sem við bætum 1 við hana í hverri keyrslu. Við þurfum að passa að bæta við teljarann á undan if setningunni svo hann telji einnig þegar leikmaðurinn giskar á rétta tölu. Við skulum skoða tvenns konar rithátt til að bæta við breytuna teljari
, en þeir gera þó það nákvæmlega sama.
Með fyrri aðferðinni erum við í raun að segja að breytan teljari
eigi að verða summan af fyrra gildi hennar og einum.
Hinn rithátturinn gerir (eins og fram kom áður) það nákvæmlega sama, en er örlítið fljótlegri og mörgum finnst hann líta betur út (dæmi þó hver fyrir sig).
Þessi aðferð virkar einnig fyrir fleiri reikniaðgerðir eins og frádrátt (-=
), margföldun (*=
) og fleira.
Þegar við setjum teljarasetninguna inn í while lykkjuna þurfum við að passa að við bætum við teljarann áður en if setningin keyrir.
Ef við rifjum aðeins upp forsendur þess að while lykkjan keyri ítrekað (while leikur_i_gangi == True:
) og skoðum hvað gerist þegar leikmaðurinn giskar á rétta tölu, þá sjáum við að ef gisk
og leynitala
bera sömu gildi (eru eins), þá breytist gildi breytunnar leikur_i_gangi
í False
. Þegar þetta gerist, þá hættir while lykkjan að keyra. Þetta þýðir að ef teljarasetningin (teljari += 1
) kemur ekki fyrir á undan if setningunni þá bætist ekki við teljarann í því tilfelli sem notandinn giskar á rétta tölu. Þannig yrði teljarinn alltaf einni tölu á eftir og yrði meira að segja núll ef notandinn giskar á rétta tölu í fyrstu tilraun.
Nú þegar við erum komin með breyturnar fyrir nafn leikmanns ásamt teljaranum skulum við gera eitthvað við þær.
Við skulum breyta skilaboðunum, þegar leikmaður giskar á rétta tölu, þannig að nafn hans og fjöldi tilrauna komi fram.
Í stað þess að skilboðin séu Jei, þú giskaðir á rétta tölu! skulum við láta þau vera Vel gert {nafn}, talan var einmitt {tala}!.
Til að prenta gildi breytanna nafn_leikmanns
og teljari
innan strengsins skulum við skoða tvær aðferðir.
Með fyrri aðferðinni skiptum við strengnum í þrjá hluta og prentum gildi breytanna á milli þeirra. Við skeytum saman hlutum strengsins , nafns leikmannsins og leynitölunni. Ef við reynum að gera það eins og í dæminu hér að neðan lendum við í smá vandræðum:
Við fáum villuboð:
Þessi villa er vegna þess að við reyndum að „leggja saman“ streng og heiltölu. Þetta leysum við með því að umbreyta heiltölunni leynitala
í streng þegar við prentum hana. Það gerum við með str() fallinu:
Seinni aðferðin sem við ætlum að skoða núna er að mörgu leyti einfaldari og líklega sú sem flestir kjósa. Hún byggir á frátektarstöðum (e. placeholders) og strengjafallinu format(). Meira um fallið format() í kaflanum um gagnatög og föll fyrir strengi. Takið eftir, í dæminu hér að neðan, að með þessari aðferð þurfum við ekki að hafa áhyggjur af tagi breytanna sem við viljum skeyta saman við strenginn.
Að lokum skulum við segja leikmanninum hvað hann þurfti margar tilraunir til að giska á leynitöluna. Aðferðin er sú sama og þegar við prentuðum nafn leikmanns og leynitöluna.
Endanleg mynd
Þegar hingað er komið höfum við náð fram öllum þeim eiginleikum forritsins sem við stefndum upphaflega að. Forritið okkar gæti þá litið einhvernveginn svona út:
Það getur reynst mjög gagnlegt að prófa að leysa verkefni aftur og reyna þá að leysa það með því að styðjast sem minnst við leiðbeiningarnar. Einnig getur verið gagnlegt að reyna að fá forritið til að hrynja (e. crash), að reyna að ná fram villuboðum með alls konar jaðartilvikum (e. edge cases). Svo þarf að leita leiða til að lagfæra þessar villur. Hvað gerist til dæmis ef notandinn slær inn bókstafi í stað tölustafa þegar hann giskar á töluna? Er eitthvað sem við getum gert til að komast hjá því að forritið hrynji ef notandinn gerir mistök?
Last updated