Loeng 4: if valikuavaldis, rekursiivne funktsioon, kõrgemat järku funktsioon, anonüümne funktsioon ja lausend lambda, and ja or operaatorite rakendamine mitte-booli andmetüüpidele¶

Viimati uuendatud 23.09.2024.

Koodinäited¶

if valikuavaldis¶

Python võimaldab lühemat (üherealist) if-else ja if-elif-else blokkide esitust. if valikuavaldist kasutatkse näiteks funktsioonide argumendi sulgudes.

Näited 1: Võrdleme eelmine nädal õpitud if-else blokki uue valikuavaldisega.

In [1]:
a = 1  # Eelmise nädala kood.

if 0 < a < 10:  # if-else blokk.
    print('1')
else:
    print('2')
1
In [2]:
print('1' if 0 < a < 10 else '2')  # Valikuavaldis funk. print sulgudes.
1
In [3]:
('1' if 0 < a < 10 else '2')  # Valikuavaldis.
Out[3]:
'1'
In [4]:
'1' if 0 < a < 10 else '2'  # Töötab ka ilma sulgudeta.
Out[4]:
'1'
In [5]:
print('1') if 0 < a < 10 else print('2')  # Valikuavaldis.
1

Näide 2: Võrdleme eelmine nädal õpitud if-elif-else blokki uue valikuavaldisega.

In [6]:
a = 3

if a == 1:  # if-elif-else blokk
    print('1')
elif a == 2:
    print('2')
else:
    print('3')
3
In [7]:
print('1' if a == 1 else '2' if a == 2 else '3')  # Valikuavaldis funk. print sulgudes.
3
In [8]:
('1' if a == 1 else '2' if a == 2 else '3')  # Valikuavaldis.
Out[8]:
'3'
In [9]:
'1' if a == 1 else '2' if a == 2 else '3'  # Töötab ka ilma sulgudeta.
Out[9]:
'3'
In [10]:
print('1') if a == 1 else print('2') if a == 2 else print('3')  # Valikuavaldis.
3

Näide 3: Võrdleme eelmine nädal õpitud if-elif-elif-else blokki uue valikuavaldisega.

In [11]:
a = 4

if a == 1:  # if-elif-elif-else blokk
    print('1')
elif a == 2:
    print('2')
elif a == 3:
    print('3')
else:
    print('4')
4
In [12]:
t1 = a == 1
t2 = a == 2
t3 = a == 3
print('1' if t1 else '2' if t2 else '3' if t3 else '4')  # Valikuavaldis funk. print sulgudes.
4
In [13]:
('1' if t1 else '2' if t2 else '3' if t3 else '4')  # Valikuavaldis.
Out[13]:
'4'
In [14]:
'1' if t1 else '2' if t2 else '3' if t3 else '4'  # Töötab ka ilma sulgudeta.
Out[14]:
'4'
In [15]:
print('1') if t1 else print('2') if t2 else print('3') if t3 else print('4')  # Valikuavaldis.
4

Rekursioon ja rekursiivne funktsioon (recursive function)¶

Rekursioon on mingi objekti kordamine ennastkopeerival teel. Reaalses elus saab rekursiooni näha näiteks veebikaamera pööramisel kuvarile, millel on pilt, vt. Joonis 1.

No description has been provided for this image
Joonis 1: Rekursiooni näide.

Termin rekursioon on valdavalt kasutusel arvutiteaduses ja matemaatikas. Rekursioon on olukord, kus näiteks arvutiprogrammis defineeritud funktsioon kutsub iseennast välja, et lahendada funktsioonile antud probleemi kergem variant. Rekursiooni astmeid võib olla lõputult palju, aga korrektse rekursiivse funktsiooni puhul on alati defineeritud baasjuhtum, mille korral rekursioon peatub. Kui baasjuhtumit ei defineerita, siis on rekursioon lõputu.

Nagu mainitud eespool on ka Pythoni rekursiivne funktsioon selline funktsioon mille definitsioonis esineb see funktsioon ise. Näiteks:

$$f(x) \doteq g(f(x)),$$

kus $f$ ja $g$ on funktsioonid, kusjuures funktsioon $g$ saab olla ka identsusfunktsioon ($g(x) = x$), ja $x$ on argument. Pythoni def blokk lubab funktsiooni definitsioonis kasutada loodavat funktsiooni rekursiivselt.

Faktoriaal¶

Faktoriaal on lihtne mitte-lõpmatult ehk lõpliku rekursiivse funktsiooni näide.

$$n! = n \cdot (n-1) \cdot (n-2) \cdot (n-3) \cdot \ldots \cdot 3 \cdot 2 \cdot 1,$$

kus $n > 1$ on täisarv. Näiteks

$$5! = 5 \cdot 4 \cdot 3 \cdot 2 \cdot 1 = 120.$$

Rekursiivne lahenduseeskiri on kujul

$$\boxed{n! \doteq n \cdot (n-1)!},$$

kus $n > 1$ on täisarv ja $1! = 1$. Joonis 2 kujutab faktoriaali rekursiivset lahendust juhul $n = 5$.

No description has been provided for this image
Joonis 2: $5!$ rekursiivne lahendus.

Pythonis defineerime arvu faktoriaali arvutava funktsiooni järgmiselt:

In [16]:
def factorial(n):
    if n <= 1:
        return 1  # Loomulik rekursiooni lõpp.
    else:
        return n * factorial(n - 1)  # NB! kasutame funktsiooni nime.

factorial(5)
Out[16]:
120
In [17]:
def factorial(n):
    if n == 1:
        print('Sisenen if: Rekursiooni lõpp.')  # Loomulik rekursiooni lõpp.
        return 1
    else:
        print('Sisenen else')
        return n * factorial(n - 1)

factorial(5)
Sisenen else
Sisenen else
Sisenen else
Sisenen else
Sisenen if: Rekursiooni lõpp.
Out[17]:
120

Rekursiivne astmefunktsioon¶

Lõpmatu rekursiooni näiteks on arvu rekursiivne ruutu tõstmine. Rekursiivne ruututõstev funktsioon on kujul:

$$f(x) \doteq f^2(x) \equiv [f(x)]^2,$$

kus $x\in \mathbb{R}$ on funktsiooni argument, vt. Joonis 3. Rekursiivse astendamise protsessi saame ülesse kirjutada näiteks kasutades kujutust. Lõpmatuses oleva kujutise liikme arvutuse eeskiri on järgmine:

$$x_{n+1} = x_n^2, \quad n \to \infty ,$$

kus $n \in \mathbb{Z}^+$ on kujutuse iteratsiooni number ja $x_n \in \mathbb{R}$. Matemaatikast teame, et kui valida algtingimuseks $x_0 < |1|$ siis

$$\lim_{n \to \infty} x_n = x_{\infty} = 0.$$

Joonis 3 kujutab osaliselt lõputut rekursiooni mis tekib eelmainitud kujutuse ehk ruututõstva astmefunktsiooni rekursiivsel rakendamisel.

No description has been provided for this image No description has been provided for this image
Joonis 3: Lõpmatu ja koonduv rekursiivne arvu ruutu tõstmine ilmutamata (vasakul) ja ilmutatud (paremal) kujul, juhul kui $x_{0} = 0.2$.

Lõpmatut rekursiooni pole võimalik Pythonis teostada. Me peame rekursiooni mingil hetkel peatama:

In [18]:
def recursive_square(x):
    print('Vahetulemus:', x)
    if x < 7e-12:  # Lõpetame rekursiooni.
        return 0
    else:
        x **= 2  # x = x**2
        return recursive_square(x)

recursive_square(0.2)
Vahetulemus: 0.2
Vahetulemus: 0.04000000000000001
Vahetulemus: 0.0016000000000000007
Vahetulemus: 2.560000000000002e-06
Vahetulemus: 6.553600000000011e-12
Out[18]:
0

Märkus: Järgmise nädala loengus arutame itereerimist Pythonis. Programmi või alamprotseduuri nimetame rekursiivseks kui selle mingi osa taaskasutab iseennast. Programmi või alamprotseduuri nimetame iteratiivseks kui selles esineb mingi tegevuse kordus ehk tsükkel. Kaks eelnevat näidet, faktoriaali ja rekursiivse astmefunktsiooni võtmine, on võimalik lahendada kasutades iteratsiooni.

Kõrgemat järku funktsioon¶

Kõrgemat järku funktsioon võtab argumendina vastu funktsioone (mitte nende väärtusi) ja/või väljastab funktsioone (mitte nenede väärtusi). Rekursiivne funktsioon on seega kõrgemat järku funktsiooni erijuht.

In [19]:
def ruut(x):
    return x**2

def func0(x, func):  # Argument func on funktsioon.
    return func(x)   # Väljastab funktsiooni väärtuse.

func0(3, ruut)
Out[19]:
9
In [20]:
def func0(x, func):
    return func(x), func  # Väljastab väärtuse ja funktsiooni.

func0(2, ruut)
Out[20]:
(4, <function __main__.ruut(x)>)

Anonüümne funktsioon ja lausend lambda ($\lambda$ calculus)¶

Anonüümne funktsioon on ilma nimeta funktsioon (soovi korral võid talle siiski nime anda). Anonüümseid funktsioone kasutame tavaliselt kõrgemat järku funktsioonide argumentidena. Anonüümne funktsioon defineeritakse ühes reas, süntaks on kujul:

lambda <argument või argumendid>: <funktsiooni algoritm>

Näide 1: Identsusfunktsioon.

In [21]:
lambda x: x  # lambda <argument>: <algoritm>
Out[21]:
<function __main__.<lambda>(x)>
In [22]:
(lambda x: x)(5)  # Argumendi väärtuse edastamine.
Out[22]:
5
In [23]:
identity = lambda x: x  # Annan nime anonüümsele funktsioonile.
identity(5)             # Kasuta nagu tavalist funktsiooni.
Out[23]:
5

Näide 1 on ekvivalentne järgmise def blokiga:

In [24]:
def identity(x):
    return x

identity(5)
Out[24]:
5

Näide 2: Ruutfunktsioon.

In [25]:
lambda x: x**2
Out[25]:
<function __main__.<lambda>(x)>
In [26]:
(lambda x: x**2)(5)
Out[26]:
25
In [27]:
square = lambda x: x**2
square(5)
Out[27]:
25

Näide 2 on ekvivalentne järgmise def blokiga:

In [28]:
def square(x):
    return x**2

square(5)
Out[28]:
25

Näide 3: Mitu argumenti, kahe muutuja summeerimine.

In [29]:
lambda x, y: x + y  # Argumente eristame komaga.
Out[29]:
<function __main__.<lambda>(x, y)>
In [30]:
(lambda x, y: x + y)(5, 2)  # Argumentide väärtuste edastamine.
Out[30]:
7
In [31]:
my_sum = lambda x, y: x + y
my_sum(5, 2)
Out[31]:
7

Näide 3 on ekvivalentne järgmise def blokiga:

In [32]:
def my_sum(x, y):
    return x + y

my_sum(5, 2)
Out[32]:
7

Anonüümse funktsiooni kasutamine kõegemat järku funktsiooni argumendina¶

Kõrgemat järku funktsioonid saavad olla nii meie enda loodud, Pythonisse sisseehitatud või kolmandate osapoolte loodud funktsioonid.

In [33]:
def high_ord_func(x, funk):
    return x + funk(x)

high_ord_func(2, lambda x: x**2)  # Defineerin funktsiooni argumentide sulgudes.
Out[33]:
6

Kõrgemat järku anonüümne funktsioon¶

In [34]:
(lambda x, y, func: x + y + func(x, y))(5, 2, my_sum)  # Funktsioon my_sum defineeritud eespool.
Out[34]:
14
In [35]:
high_ord_func = lambda x, func: x + func(x)
high_ord_func(2, lambda x: x * x)
Out[35]:
6

Sama tulemus kasutades def-return blokki:

In [36]:
def ruut(x):
    return x * x

def high_ord_func(x, funk):
    return x + funk(x)

high_ord_func(2, ruut)
Out[36]:
6

Rekursiivne anonüümne funktsioon¶

Selleks, et luua rekursiivne anonüümne funktsioon peame sellele nime andma ning seda funktsiooni definitsioonis kasutama. Lisaks kasutame if-else valikuavaldist.

Näide: Faktoriaali arvutamine.

In [37]:
fact = lambda n: 1 if n <= 1 else n * fact(n - 1)
fact(5)
Out[37]:
120

and ja or operaatorite rakendamine mitte-booli tõeväärtustele¶

Loogikaoperaatoreid and ja or saab rakendada kõikidele Pythoni andmetüüpidele. Meile juba teada reegel: triviaalse objekti (0, [], {}, (), '', jne.) tõeväärtus on $0$ (False) ja kõikide muude mitte-triviaalsete andmetüüpide tõväärtus on $1$ (True). Väärtuse leidmiseks kasuta sisseehitatud funktsiooni bool.

Lisaks kehtivad järgmised seosed:

Operaatori or rakendamine sõnedele (või muudele andmetüüpidele):

In [38]:
'a' or 'b'  # = 'a'
Out[38]:
'a'
In [39]:
'' or ''  # = parempoolne triviaalne objekt.
Out[39]:
''
In [40]:
'' or 'b'  # = 'b'
Out[40]:
'b'
In [41]:
'a' or ''  # = 'a'
Out[41]:
'a'

Toetatakse ka segatehete kasutust:

In [42]:
'A' or []  # Mittetriviaalne sõne ja triviaalne list.
Out[42]:
'A'

Operaatori and rakendamine sõnedele (või muudele andmetüüpidele):

In [43]:
'a' and 'b'  # = 'b'
Out[43]:
'b'
In [44]:
'' and ''  # = vasakpoolne triviaalne objekt.
Out[44]:
''
In [45]:
'' and 'b'  # = ''
Out[45]:
''
In [46]:
'a' and ''  # = ''
Out[46]:
''

Kuidas kasutada?

Näide 1: Kui loend pole tühi lisa sellele sõne 'abc'. Alustame meile juba teadaoleva lähenemisega.

In [47]:
a = []
b = ['a']

if len(a) > 0:  # Funktsioon len sisseehitatud funktsioon.
    a.append('abc')  # Meetod append on listi meetod.
elif len(b) > 0:
    b.append('abc')

print(a, b)
[] ['a', 'abc']

Lahend kasutades loogikaoperaatoreid:

In [48]:
a = []
b = ['a']

(a or b or []).append('abc')  # NB! tehete järjekord.

print(a, b)
[] ['a', 'abc']

Näide 2: Leiame arvu paarsuse.

In [49]:
x = 2
x % 2 and 'paaritu' or 'paaris'
Out[49]:
'paaris'
In [50]:
x = 2
print(x % 2 and 'paaritu' or 'paaris')
paaris
In [51]:
x = 2
x % 2 and print('paaritu') or print('paaris')
paaris
In [52]:
paarsus = lambda x: x % 2 and 'paaritu' or 'paaris'
print(paarsus(3))
paaritu

NB! Pikemate tehete korral pea meeles, et operaator and on operaatorist or prioriteetsem, vt. Loeng 3 koodinäiteid.


Ruutvõrrandi lahendist¶

Ruutvõrrand on antud kujul:

$$a x^2 + b x + c = 0,$$

kus $x$ on tundmatu, $a$, $b$ ja $c$ on etteantud koefitsendid. Ruutvõrrandi üldlahend on antud kujul:

$$x_\pm = \frac{-b \pm \sqrt{\Delta}}{2 a},$$

kus lahendi käitumine sõltub diskriminandi

$$\Delta = b^2 - 4ac$$

väärtusest. Lahendi sõltuvus diskriminandist $\Delta$ ja kordajatest $a$, $b$, $c$ on järgmine:

  1. Kui $a = 0$ siis pole tegu ruutvõrrandiga.

  2. Kui $\Delta > 0$ eksisteerivad reaalsed lahendid: $$x_\pm = \frac{-b \pm \sqrt{\Delta}}{2a}.$$

  3. Kui $\Delta = 0$ siis eksisteerib üks reaalne lahend: $$x_+ = x_- = -\frac{b}{2a}.$$

  4. Kui $\Delta < 0$ siis eksisteerivad kompleksarvulised lahendid kujul: $$x_{\pm} = -\frac{b}{2a} \pm \frac{\sqrt{-\Delta}}{2a}{\rm i},$$ kus ${\rm i}$ on imaginaarühik.

Lahendi käitumine on illustreeritud Joonisel 4.

No description has been provided for this image
Joonis 4: Ruutvõrrandi lahendi sõltuvus diskriminandist $\Delta$.

Eelmine nädal lõime funktsiooni mis lahendab ruutvõrrandit. Funktsioon omas järgmist kuju:

In [53]:
def sol(a, b, c):
    d = (b**2 - 4*a*c)**0.5
    x1 = (-b + d) / (2 * a)  # Üldlahend.
    x2 = (-b - d) / (2 * a)
    return x1, x2

sol(-1, 2, 3)
Out[53]:
(-1.0, 3.0)

















☻   ☻   ☻