> For the complete documentation index, see [llms.txt](https://shsteindorsson.gitbook.io/med-verk/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://shsteindorsson.gitbook.io/med-verk/verkefni/fizzbuzz.md).

# FizzBuzz

Þetta verkefni gengur út á að skrifa lítið forrit sem fer í gegn um (skoðar) talnarunu og prentar texta eða tölu eftir ákveðnum skilyrðum.

* Ef viðkomandi tala er deilanleg með 3 er prentaður strengurinn `Fizz`.
* Sé hún deilanleg með 5 prentast `Buzz`.
* Gangi *bæði* 3 *og* 5 upp í hana prentast `FizzBuzz`.
* En ef *hvorki 3 né 5* ganga upp í töluna, þá er *talan sjálf* prentuð. &#x20;

Ef tala er deilanleg með einhverri tölu, til dæmis 3, þýðir það að við skipt henni, til dæmis í þrennt, og það verður enginn afgangur.\
Þetta er klassískt verkefni sem er meira að segja stundum notað í atvinnuviðtölum. Það sem mörgum finnst svo skemmtilegt við þetta verkefni er hversu margar leiðir er hægt að fara til að leysa það.\
Við munum núna skoða eina leið til að leysa verkefnið með glæsibrag ;)

#### Í þessu verkefni koma fyrir eftirfarandi þættir:

* for-lykkja
* heiltölur
* strengir
* if-elif-else setningar
* stofnleif (e. modulus)
* tagskipti

### Hverju stefnum við að?

Virkni einfaldrar útgáfu þessa forrits gæti litið svona út:

```python
Sláðu inn hæstu töluna: 20
Sláðu inn lægstu töluna: 1
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
```

### Hvar eigum við að byrja?

Ef skoðum inngangsorð þessa verkefnis þá sjáum við að þar er talað um *talnarunu*. Til að fá margar tölur í röð (talnarunu), þá er tilvalið að nota for-lykkju (meira um for-lykkjur í kafla um lykkjur og skilyrðingar).

#### for-lykkjan

Eins og við vitum þá þurfa for-lykkjur eitthvað til að „endurtaka“ og því ætlum við í þessu dæmi að nota innbyggða fallið `range()` til þess. Fallið `range()` þarf minnst eitt inntaksgildi (e. argument) en við ætlum að nota tvö, annað sem upphafstölu og hitt sem endatölu (*meira um* `range()` *í kafla um innbyggð föll*).\
Lykkjusetningin, þar sem talið er frá 1 og að 20, gæti þá litið svona út:

```python
for tala in range(1, 20):
```

#### skilyrtar setningar

Þá er komið að því að skilgreina hvað gerist ef skilyrðum okkar er mætt.\
Við ætlum að nota reikniaðgerð sem kallast stofnleif (e. modulus) en hún skilar okkur alltaf *afganginum*.

**Dæmi:**

`10 % 3 = 1` | Vegna þess að 3 gengur þrisvar sinnum upp í 10 (3x3=9) og þá er einn í afgang.

Við erum sem sagt að leita eftir tölum þar sem **enginn afgangur** er þegar við deilum 3 eða 5.

**Hverju erum við að leita eftir?**

Nú vitum við að tilvikin sem við erum að leita eftir eru þrjú:

* Tala er deilanleg með 3 og 5
* Tala er einungis deilanleg með 3
* Tala er einungis deilanleg með 5

Þá skulum við raða þessum tilvikum í `if` og `elif` setningar.\
Við látum strangasta/þrengsta skilyrðið koma fyrst í `if` setningu vegna þess að komi það upp, þá er óþarfi að kanna önnur skilyrði. `if` setningin gæti þá litið svona út:

```python
for tala in range(1, 20):
    if tala % 3 == 0 and tala % 5 == 0:
        print('FizzBuzz')
```

Takið eftir `and` skilyrðingunni í `if` setningunni, hún gerir okkur kleift að setja tvær skilyrðingar í eina setningu. Við hefðum getað hreiðrað aðra `if` setningu undir þá fyrri og fengið þannig sömu virkni en það hefði leitt af sér ljótari kóða sem væri ekki eins læsilegur. Það hefði þá litið svona út:

```python
for tala in range(1, 20):
    if tala % 3 == 0:
        if tala % 5 == 0:
            print('FizzBuzz')
```

![](/files/-LNR8UyNX-4Cu7iuRVm8)

Næst skulum við bæta við `elif` setningum fyrir tilfelli þar sem talan er *ekki deilanlega með bæði* 3 *og* 5 en er samt deilanleg með *annað hvort* 3 *eða* 5:

```python
for tala in range(1, 20):
    if tala % 3 == 0 and tala % 5 == 0:
        print('FizzBuzz')
    elif tala % 3 == 0:
        print('Fizz')
    elif tala % 5 == 0:
        print('Buzz')
```

Við megum ekki gleyma að prenta töluna sjálfa í þeim tilfellum sem `if` og `elif` setningarnar eiga ekki við. Þá bætum við `else` setningu í lokinn sem prentar töluna:

```python
for tala in range(1, 20):
    if tala % 3 == 0 and tala % 5 == 0:
        print('FizzBuzz')
    elif tala % 3 == 0:
        print('Fizz')
    elif tala % 5 == 0:
        print('Buzz')
    else:
        print(tala)
```

Þá er grunnvirkni forritsins komin, fyrst athugar það hvort talan sé bæði deilanleg með 3 og 5, ef ekki, þá hvort talan sé deilanleg með annað hvort 3 eða 5. Ef ekkert af þessu á við þá er talan sjálf prentuð.

Í upphafi verkefnis, þar sem við sáum virknina sem við stefndum að, þá sést forritið spyrja notandann um talnabilið. Við skulum nú bæta þeirri virkni við forritið okkar.\
Fallið `range()` tekur inn tvö gildi sem eru annars vegar fyrsta talan og hins vegar talan sem við viljum telja að. Ef við skilgreinum tvær breytur fyrir þessi gildi getum við notað þær sem inntaksgildi (e. argument) fyrir `range()` fallið.\
Köllum þar bara `l_tala` (fyrir „lægri tala“) og `h_tala` (fyrir „hærri tala“) en þær geta auðvitað heitað hvað sem er. Við gefum þeim ekkert gildi heldur notum `input()` fallið sem sér um að fá gildi frá notandanum. Forritið okkar gæti því litið svona út:

```python
l_tala = input('Sláðu inn lægstu töluna: ')
h_tala = input('Sláðu inn hæstu töluna: ')

for tala in range(l_tala, h_tala):
    if tala % 3 == 0 and tala % 5 == 0:
        print('FizzBuzz')
    elif tala % 3 == 0:
        print('Fizz')
    elif tala % 5 == 0:
        print('Buzz')
    else:
        print(tala)
```

Ef við prófum að keyra forritið lendum við í smá vandræðum:

```python
Sláðu inn lægstu töluna: 1
Sláðu inn hæstu töluna: 20
Traceback (most recent call last):
  File "/home/sverrir/py_verkefni/fizzbuzz.py", line 4, in <module>
    for tala in range(l_tala, h_tala):
TypeError: 'str' object cannot be interpreted as an integer
```

![Munið að túlkurinn er til að hjálpa en ekki skamma!](/files/-LNR9H9828GbO5DZatSB)

Við sjáum neðst í þessum villuboðum að við fengum tagvillu eða `TypeError` sem segir okkur að einhvers staðar erum við að nota rangt tag (e. type). Villuboðin vísa einnig í fjórðu línu forritsins og birta jafnframt þá línu:

```python
for tala in range(l_tala, h_tala):
```

Þessi er villa er vegna þess að fallið range tekur inn heiltölugildi (og ekkert annað!) en þegar við notum fallið `input()` fáum við alltaf streng.

### Ábending - skilagildi input() fallsins er alltaf strengur

Því þurfum við að breyta tagi `l_tala` og `h_tala` í heiltölu og við gerum það með fallinu `int()`. Það skiptir ekki mála hvar við gerum það, við gætum bæði gert það þegar við skilgreinum breyturnar:

```python
h_tala = int(input('Sláðu inn hæstu töluna: '))
l_tala = int(input('Sláðu inn lægstu töluna: '))
```

Við getum einnig gert það þegar við köllum á `range()` fallið:

```python
for tala in range(int(l_tala), int(h_tala)):
```

Þá erum við komin að lokum þessa verkefnis og forritið okkar gæti þá litið einhvernveginn svona út:

```python
h_tala = int(input('Sláðu inn hæstu töluna: '))
l_tala = int(input('Sláðu inn lægstu töluna: '))

for tala in range(l_tala, h_tala):
    if tala % 3 == 0 and tala % 5 == 0:
        print('FizzBuzz')
    elif tala % 3 == 0:
        print('Fizz')
    elif tala % 5 == 0:
        print('Buzz')
    else:
        print(tala)
```

Munið að prófa að leysa verkefnið án leiðbeininga, skoðið leiðbeiningarnar eftir þörfum. Önnur nálgun við verkefnið getur verið að breyta forritinu í fall. Til að þyngja verkefnið má til dæmis leita leiða til að koma í veg fyrir að forritið hrynji ef notandinn slær ekki inn tölustafi fyrir hæstu eða lægstu tölurnar.
