V oni stari nalogi imamo dva vrča; v vrč a gre 7 litrov in v vrč b gredo 4 litri. Vodo lahko točimo:

Naloga običajno zahteva, da z vrčema namerimo določeno količino vode, na primer dva litra. Naša ni takšna.

Obvezna naloga

Tole je skoraj bolj naloga iz pogojev kot iz zank.

Zaporedje pretakanj naj bo podano takole (te vrstice skopiraj na začetek programa):

poteze = [("j", "a"), ("a", "b"), ("b", "j"), ("a", "b"), ("j", "a"),
          ("a", "b"), ("b", "j"), ("a", "b"), ("a", "j"), ("b", "a"),
          ("j", "a"), ("a", "j")]

Pri tem prvi element para pove, odkod točimo, drugi pa, kam. Napiši program, ki simulira takšno pretakanje in sproti poroča, koliko vode je v katerem vrču. Za gornji primer mora izpisati:

a: 7 b: 0
a: 3 b: 4
a: 3 b: 0
a: 0 b: 3
a: 7 b: 3
a: 6 b: 4
a: 6 b: 0
a: 2 b: 4
a: 0 b: 4
a: 4 b: 0
a: 7 b: 0
a: 0 b: 0

Rešitev

Zelo malo imamo komentirati.

poteze = [("r", "a"), ("a", "b"), ("b", "j"), ("a", "b"), ("j", "a"), ("a", "b"),
          ("b", "j"), ("a", "b"), ("a", "j"), ("b", "a"), ("j", "a"), ("a", "j")]

a = b = 0
for odkod, kam in poteze:
    if odkod == "a":
        if kam == "b":
            if b + a > 4:
                a -= 4 - b
                b = 4
            else:
                b += a
                a = 0
        else:
            a = 0
    elif odkod == "b":
        if kam == "a":
            if b + a > 7:
                b -= 7 - a
                a = 7
            else:
                a += b
                b = 0
        else:
            b = 0
    else:  # odkod == "j"
        if kam == "a":
            a = 7
        else:  # kam == "b"
            b = 4
    print("a:", a, "b:", b)
a: 7 b: 0
a: 3 b: 4
a: 3 b: 0
a: 0 b: 3
a: 7 b: 3
a: 6 b: 4
a: 6 b: 0
a: 2 b: 4
a: 0 b: 4
a: 4 b: 0
a: 7 b: 0
a: 0 b: 0

Se da kaj poenostaviti? Pogojev pri pretakanju med posodama se da izogniti z uporabo funkcije min: pretočimo toliko, kolikor je v a ali toliko, kolikor gre v b, kar je pač manjše.

poteze = [("r", "a"), ("a", "b"), ("b", "j"), ("a", "b"), ("j", "a"), ("a", "b"),
          ("b", "j"), ("a", "b"), ("a", "j"), ("b", "a"), ("j", "a"), ("a", "j")]

a = b = 0
for odkod, kam in poteze:
    if odkod == "a":
        if kam == "b":
            kolicina = min(a, 4 - b)
            a, b = a - kolicina, b + kolicina
        else:
            a = 0
    elif odkod == "b":
        if kam == "a":
            kolicina = min(b, 7 - a)
            a, b = a + kolicina, b - kolicina
        else:
            b = 0
    else:  # odkod == "j"
        if kam == "a":
            a = 7
        else:  # kam == "b"
            b = 4
    print("a:", a, "b:", b)
a: 7 b: 0
a: 3 b: 4
a: 3 b: 0
a: 0 b: 3
a: 7 b: 3
a: 6 b: 4
a: 6 b: 0
a: 2 b: 4
a: 0 b: 4
a: 4 b: 0
a: 7 b: 0
a: 0 b: 0

Morda pa je preprosteje, če obravnavamo posamične primere brez gnezdenja pogojev? In celo če, v nasprotju z vsem, kar poskušam študentom vbiti v glavo, ne razpakiramo terke?

a = b = 0
for poteza in poteze:
    if poteza == ("j", "a"):
        a = 7
    if poteza == ("j", "b"):
        b = 7
    if poteza == ("a", "j"):
        a = 0
    if poteza == ("b", "j"):
        b = 0
    if poteza == ("a", "b"):
        a, b = a - min(a, 4 - b), b + min(a, 4 - b)
    if poteza == ("b", "a"):
        a, b = a + min(b, 7 - a), b - min(b, 7 - a)
    print("a:", a, "b:", b)
a: 0 b: 0
a: 0 b: 0
a: 0 b: 0
a: 0 b: 0
a: 7 b: 0
a: 3 b: 4
a: 3 b: 0
a: 0 b: 3
a: 0 b: 3
a: 3 b: 0
a: 7 b: 0
a: 0 b: 0

Hm, pravzaprav je bilo tole res preprosteje.

V Pythonu 3.10 pa lahko uporabimo novi stavek match, ki program še malo skrajša.

a = b = 0
for poteza in poteze:
    match poteza:
        case "j", "a":
            a = 7
        case "j", "b":
            b = 7
        case "a", "j":
            a = 0
        case "b", "j":
            b = 0
        case "a", "b":
            a, b = a - min(a, 4 - b), b + min(a, 4 - b)
        case "b", "a":
            a, b = a + min(b, 7 - a), b - min(b, 7 - a)
    print("a:", a, "b:", b)
a: 0 b: 0
a: 0 b: 0
a: 0 b: 0
a: 0 b: 0
a: 7 b: 0
a: 3 b: 4
a: 3 b: 0
a: 0 b: 3
a: 0 b: 3
a: 3 b: 0
a: 7 b: 0
a: 0 b: 0

Dodatna naloga

Zdaj pa obratno: imamo zaporedje stanj vrčev, podanih s seznamom terk, na primer.

stanja = [(7, 0), (3, 4), (3, 0), (0, 3), (7, 3), (6, 4),
          (6, 0), (2, 4), (0, 4), (4, 0), (7, 0), (0, 0)]

Napiši program, ki za takšno zaporedje izpiše pripadajoči vrstni red pretakanj. Izpis naj bo v takšni obliki:

j a
a b
b j
a b
j a
a b
b j
a b
a j
b a
j a
a j

Rešitev

Osnovna ideja je, da moramo primerjati prejšnje in novo stanje ter izpisati ustrezno spremembo.

seq = [(7, 0), (3, 4), (3, 0), (0, 3), (7, 3), (6, 4), (6, 0), (2, 4), (0, 4), (4, 0), (7, 0), (0, 0)]
a = b = 0

for na, nb in seq:  # na in nb sta novi a in novi b
    if a == na:  # a je ostal enak, nekaj se dogaja z b
        if nb == 0:
            print("b j")
        else:
            print("j b")
    elif b == nb:  # b je ostal enak, nekaj se dogaja z a
        if na == 0:
            print("a j")
        else:
            print("j a")
    else:  # sicer točimo med posodama
        if na > a:  # količina v a se je povečala ...
            print("b a")
        else:
            print("a b")
    a, b = na, nb
j a
a b
b j
a b
j a
a b
b j
a b
a j
b a
j a
a j