From 29bc34810cb1d1fbd208f6b5f2dad73188ca7f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Mo=C5=BEina?= Date: Tue, 11 Oct 2016 14:39:50 +0200 Subject: - new exercises on recursion - added text (lectures) on conditions and loops --- python/problems/recursion/binary/common.py | 75 ++++++ python/problems/recursion/binary/en.py | 13 + python/problems/recursion/binary/sl.py | 30 +++ python/problems/recursion/factorial/common.py | 75 ++++++ python/problems/recursion/factorial/en.py | 13 + python/problems/recursion/factorial/sl.py | 37 +++ python/problems/recursion/sum/common.py | 70 +++++ python/problems/recursion/sum/en.py | 13 + python/problems/recursion/sum/sl.py | 32 +++ python/problems/while_and_if/conditions_sl.html | 159 ++++++++++++ python/problems/while_and_if/ifclause_sl.html | 330 ++++++++++++++++++++++++ python/problems/while_and_if/loops_sl.html | 179 +++++++++++++ python/problems/while_and_if/sl.py | 4 +- 13 files changed, 1029 insertions(+), 1 deletion(-) create mode 100644 python/problems/recursion/binary/common.py create mode 100644 python/problems/recursion/binary/en.py create mode 100644 python/problems/recursion/binary/sl.py create mode 100644 python/problems/recursion/factorial/common.py create mode 100644 python/problems/recursion/factorial/en.py create mode 100644 python/problems/recursion/factorial/sl.py create mode 100644 python/problems/recursion/sum/common.py create mode 100644 python/problems/recursion/sum/en.py create mode 100644 python/problems/recursion/sum/sl.py create mode 100644 python/problems/while_and_if/conditions_sl.html create mode 100644 python/problems/while_and_if/ifclause_sl.html create mode 100644 python/problems/while_and_if/loops_sl.html diff --git a/python/problems/recursion/binary/common.py b/python/problems/recursion/binary/common.py new file mode 100644 index 0000000..585c5ff --- /dev/null +++ b/python/problems/recursion/binary/common.py @@ -0,0 +1,75 @@ +import re +from python.util import has_token_sequence, string_almost_equal, \ + string_contains_number, get_tokens, get_numbers, get_exception_desc, \ + all_tokens, has_comprehension, has_loop, almost_equal, get_ast +from server.hints import Hint + +id = 20808 +number = 8 +visible = True + +solution = '''\ +def binary(n): + if n <= 1: + return str(n) + return binary(n // 2) + str(n % 2) +''' + +hint_type = { + 'final_hint': Hint('final_hint'), + 'has_loop': Hint('has_loop') +} + +def test(python, code, aux_code=''): + func_name = 'binary' + tokens = get_tokens(code) + ast = get_ast(code) + if not has_token_sequence(tokens, ['def', func_name]): + return False, [{'id' : 'no_func_name', 'args' : {'func_name' : func_name}}] + + in_out = [ + (3, '11'), + (2, '10'), + (1, '1'), + (0, '0'), + (42, '101010') + ] + + test_in = [('{0}({1})'.format(func_name, str(l[0])), None) + for l in in_out] + test_out = [l[1] for l in in_out] + + answers = python(code=aux_code+code, inputs=test_in, timeout=1.0) + n_correct = 0 + tin, tout = None, None + for i, (ans, to) in enumerate(zip(answers, test_out)): + res = ans[0] + corr = res == to + n_correct += corr + if not corr: + tin = test_in[i][0] + tout = to + + passed = n_correct == len(test_in) + hints = [{'id': 'test_results', 'args': {'passed': n_correct, 'total': len(test_in)}}] + if tin: + hints.append({'id': 'problematic_test_case', 'args': {'testin': str(tin), 'testout': str(tout)}}) + if passed: + if has_loop(ast): + hints.append({'id': 'has_loop'}) + else: + hints.append({'id': 'final_hint'}) + + + return passed, hints + + +def hint(python, code, aux_code=''): + tokens = get_tokens(code) + + # run one test first to see if there are any exceptions + answer = python(code=aux_code+code, inputs=[(None, None)], timeout=1.0) + exc = get_exception_desc(answer[0][3]) + if exc: return exc + + return None diff --git a/python/problems/recursion/binary/en.py b/python/problems/recursion/binary/en.py new file mode 100644 index 0000000..01440f7 --- /dev/null +++ b/python/problems/recursion/binary/en.py @@ -0,0 +1,13 @@ +id = 20808 +name = 'Binary' + +description = '''\ +

(translation missing)

''' + +hint = { + 'plan': '''\ +

(translation missing)

''', + + 'no_input_call': '''\ +

(translation missing)

''', +} diff --git a/python/problems/recursion/binary/sl.py b/python/problems/recursion/binary/sl.py new file mode 100644 index 0000000..8360a46 --- /dev/null +++ b/python/problems/recursion/binary/sl.py @@ -0,0 +1,30 @@ +import server +mod = server.problems.load_language('python', 'sl') + +id = 20808 +name = 'Binarno' + +description = '''\ +

+Napiši rekurzivno funkcijo binary(n), ki kot argument prejme število +n in kot rezultat vrne niz s tem številom v dvojiškem zapisu.

+ +
+>>> binary(42)
+'101010'
+
+''' + +plan = [] + +hint = { + 'final_hint': ['''\ +

Program je pravilen!
+

+'''], + + 'has_loop': ['''\ +

Program sicer deluje pravilno, vendar vsebuje zanko. +

+'''], +} diff --git a/python/problems/recursion/factorial/common.py b/python/problems/recursion/factorial/common.py new file mode 100644 index 0000000..fec787b --- /dev/null +++ b/python/problems/recursion/factorial/common.py @@ -0,0 +1,75 @@ +import re +from python.util import has_token_sequence, string_almost_equal, \ + string_contains_number, get_tokens, get_numbers, get_exception_desc, \ + all_tokens, has_comprehension, has_loop, almost_equal, get_ast +from server.hints import Hint + +id = 20806 +number = 6 +visible = True + +solution = '''\ +def factorial(n): + if n == 0: + return 1 + return n * factorial(n - 1) +''' + +hint_type = { + 'final_hint': Hint('final_hint'), + 'has_loop': Hint('has_loop') +} + +def test(python, code, aux_code=''): + func_name = 'factorial' + tokens = get_tokens(code) + ast = get_ast(code) + if not has_token_sequence(tokens, ['def', func_name]): + return False, [{'id' : 'no_func_name', 'args' : {'func_name' : func_name}}] + + in_out = [ + (0, 1), + (1, 1), + (2, 2), + (12, 479001600), + (20, 2432902008176640000) + ] + + test_in = [('{0}({1})'.format(func_name, str(l[0])), None) + for l in in_out] + test_out = [l[1] for l in in_out] + + answers = python(code=aux_code+code, inputs=test_in, timeout=1.0) + n_correct = 0 + tin, tout = None, None + for i, (ans, to) in enumerate(zip(answers, test_out)): + res = ans[0] + corr = res == to + n_correct += corr + if not corr: + tin = test_in[i][0] + tout = to + + passed = n_correct == len(test_in) + hints = [{'id': 'test_results', 'args': {'passed': n_correct, 'total': len(test_in)}}] + if tin: + hints.append({'id': 'problematic_test_case', 'args': {'testin': str(tin), 'testout': str(tout)}}) + if passed: + if has_loop(ast): + hints.append({'id': 'has_loop'}) + else: + hints.append({'id': 'final_hint'}) + + + return passed, hints + + +def hint(python, code, aux_code=''): + tokens = get_tokens(code) + + # run one test first to see if there are any exceptions + answer = python(code=aux_code+code, inputs=[(None, None)], timeout=1.0) + exc = get_exception_desc(answer[0][3]) + if exc: return exc + + return None diff --git a/python/problems/recursion/factorial/en.py b/python/problems/recursion/factorial/en.py new file mode 100644 index 0000000..616eacd --- /dev/null +++ b/python/problems/recursion/factorial/en.py @@ -0,0 +1,13 @@ +id = 20806 +name = 'Factorial' + +description = '''\ +

(translation missing)

''' + +hint = { + 'plan': '''\ +

(translation missing)

''', + + 'no_input_call': '''\ +

(translation missing)

''', +} diff --git a/python/problems/recursion/factorial/sl.py b/python/problems/recursion/factorial/sl.py new file mode 100644 index 0000000..a0269e3 --- /dev/null +++ b/python/problems/recursion/factorial/sl.py @@ -0,0 +1,37 @@ +import server +mod = server.problems.load_language('python', 'sl') + +id = 20806 +name = 'Fakulteta' + +description = '''\ +

+Napiši rekurzivno funkcijo faktorial(n), ki izračuna n!. +Funkcija je definirana z enačbama 0! = 1 in +n! = n * (n - 1)!. +Rešitev ne sme vsebovati zanke for ali zanke while.

+
+>>> faktorial(0)
+1
+>>> faktorial(1)
+1
+>>> faktorial(5)
+120
+>>> faktorial(20)
+2432902008176640000
+
+''' + +plan = [] + +hint = { + 'final_hint': ['''\ +

Program je pravilen!
+

+'''], + + 'has_loop': ['''\ +

Program sicer deluje pravilno, vendar vsebuje zanko. +

+'''], +} diff --git a/python/problems/recursion/sum/common.py b/python/problems/recursion/sum/common.py new file mode 100644 index 0000000..9625a76 --- /dev/null +++ b/python/problems/recursion/sum/common.py @@ -0,0 +1,70 @@ +import re +from python.util import has_token_sequence, string_almost_equal, \ + string_contains_number, get_tokens, get_numbers, get_exception_desc, \ + all_tokens, has_comprehension, has_loop, almost_equal, get_ast +from server.hints import Hint + +id = 20807 +number = 7 +visible = True + +solution = '''\ +def sum(xs): + if not xs: + return 0 + if isinstance(xs[0], list): + return sum(xs[0]) + sum(xs[1:]) + return xs[0] + sum(xs[1:]) +''' + +hint_type = { + 'final_hint': Hint('final_hint') +} + +def test(python, code, aux_code=''): + func_name = 'sum' + tokens = get_tokens(code) + if not has_token_sequence(tokens, ['def', func_name]): + return False, [{'id' : 'no_func_name', 'args' : {'func_name' : func_name}}] + + in_out = [ + ([], 0), + ([1,2,3,4,5], 15), + ([1, [], [2, 3, [4]], 5], 15), + ([1, [], [2, 3, [4]], 5, [[[[10], 6], [[7], 9], 8]]], 55), + ] + + test_in = [('{0}({1})'.format(func_name, str(l[0])), None) + for l in in_out] + test_out = [l[1] for l in in_out] + + answers = python(code=aux_code+code, inputs=test_in, timeout=1.0) + n_correct = 0 + tin, tout = None, None + for i, (ans, to) in enumerate(zip(answers, test_out)): + res = ans[0] + corr = res == to + n_correct += corr + if not corr: + tin = test_in[i][0] + tout = to + + passed = n_correct == len(test_in) + hints = [{'id': 'test_results', 'args': {'passed': n_correct, 'total': len(test_in)}}] + if tin: + hints.append({'id': 'problematic_test_case', 'args': {'testin': str(tin), 'testout': str(tout)}}) + if passed: + hints.append({'id': 'final_hint'}) + + return passed, hints + + +def hint(python, code, aux_code=''): + tokens = get_tokens(code) + + # run one test first to see if there are any exceptions + answer = python(code=aux_code+code, inputs=[(None, None)], timeout=1.0) + exc = get_exception_desc(answer[0][3]) + if exc: return exc + + return None diff --git a/python/problems/recursion/sum/en.py b/python/problems/recursion/sum/en.py new file mode 100644 index 0000000..a61f900 --- /dev/null +++ b/python/problems/recursion/sum/en.py @@ -0,0 +1,13 @@ +id = 20807 +name = 'Sum' + +description = '''\ +

(translation missing)

''' + +hint = { + 'plan': '''\ +

(translation missing)

''', + + 'no_input_call': '''\ +

(translation missing)

''', +} diff --git a/python/problems/recursion/sum/sl.py b/python/problems/recursion/sum/sl.py new file mode 100644 index 0000000..e23bd97 --- /dev/null +++ b/python/problems/recursion/sum/sl.py @@ -0,0 +1,32 @@ +import server +mod = server.problems.load_language('python', 'sl') + +id = 20807 +name = 'Vsota seznama' + +description = '''\ +

+Napiši funkcijo sum(xs), ki sešteje elemente vgnezdenega seznama xs. +Pomagajte si s funkcijo isinstance.

+
+>>> isinstance([1, 2, 3], list)
+True
+>>> isinstance(1, list)
+False
+>>> sum([])
+0
+>>> sum([1, 2, 3, 4, 5])
+15
+>>> sum([1, [], [2, 3, [4]], 5])
+15
+
+''' + +plan = [] + +hint = { + 'final_hint': ['''\ +

Program je pravilen!
+

+'''], +} diff --git a/python/problems/while_and_if/conditions_sl.html b/python/problems/while_and_if/conditions_sl.html new file mode 100644 index 0000000..6d6f838 --- /dev/null +++ b/python/problems/while_and_if/conditions_sl.html @@ -0,0 +1,159 @@ + + + + + + + + + + +

Pogoji

+ +

Zdaj pa povejmo nekaj splošnejšega o pogojih. Kakšni vse so lahko? Najprej, + pogoj je izraz. Torej nekaj, kar se da izračunati (po naši garažni + definiciji izraza). Doslej so bili vsi naši izrazi nekako številski, + aritmetični: v njih so nastopala števila in le-ta smo seštevali, množili, + odštevali in kar se še takega dela s števili. Pogoji pa so logični izrazi. + (V resnici se "logičnost" in "številskost" lahko poljubno prepletata, a to + bomo mimogrede odkrili sproti.)

+ +

Rezultat logičnega izraza je logična vrednost. Logične vrednosti niso števila + (torej niso int ali float) in niso nizi, torej so + nekaj četrtega. Podatkovni tip, ki hrani logične vrednosti, se imenuje + bool (iz Boolean, po slovensko Booleov). Medtem ko je + različnih števil (int, float) kar veliko, + ima tip bool lahko le eno od dveh + vrednosti, True ali False. Tudi True + in False rezervirani besedi.

+ +

S števili že znamo računati. Z nizi tudi malo. Kako pa računamo z logičnimi + vrednostmi? Menda jih ne moremo kar tako seštevati in odštevati?

+ +

Za zdaj se delajmo, da ne. Kasneje bomo videli, da zna Python z logičnimi + izrazi početi še to in ono zanimivo reč, ki pa za začetnika niso tako + pomembne.

+ +

Za logične izraze so nam na voljo trije operatorji: or, + and in not. + +

+>>> True and True
+True
+>>> True and False
+False
+>>> False or True
+True
+>>> not False
+True
+>>> False or not False
+True
+
+ +

(and, or in not so rezervirane besede.

+ +

Operator not ima prednost pred and, + and pa pred or. Spet si lahko pomagamo z + oklepaji: a or b and c pomeni isto kot + a or (b and c) in bo resničen (True), če je resničen + a ali pa sta resnična b in c. Izraz + (a or b) and c pa bo resničen, če sta resnična a + ali b (ali oba), poleg tega pa nujno še c. Pri + tem seveda predpostavljamo, da a, b in + c vsebujejo logične vrednosti.

+ +

Da bo reč lažje razumljiva, jo potrenirajmo na prav tem izrazu, vendar tako, + da namesto a, b in c oziroma namesto + "golih" True in False pišemo bolj "konkretne" + izraze.

+ +
+>>> 352 > 0
+True
+>>> 5 < 3
+False
+>>> 352 > 0 or 5 < 3
+True
+>>> 352 > 0 or 5 > 3
+True
+>>> 352 > 0 and 5 > 3
+True
+>>> 352 > 0 and 5 < 3
+False
+>>> 352 > 0 and not 5 < 3
+True
+>>> 10 > 8 or 352 > 0 and 5 < 3
+True
+>>> 10 > 8 or (352 > 0 and 5 < 3)
+True
+>>> (10 > 8 or 352 > 0) and 5 < 3
+False
+>>> (10 > 8 or 352 > 0) and not 5 < 3
+True
+
+ +

Operatorja > in < pričakujeta na levi in desni neke reči, ki jih je + mogoče primerjati; zadovoljna nista le s števili, temveč tudi z, recimo, + nizi (primerjala jih bosta po abecedi), ne moreta pa primerjati števila z + nizom. Njun rezultat je logična vrednost. Za "večje ali enako" in "manjše + ali enako" uporabimo >= in <=.

+ +

Če želimo preveriti enakost dveh števil (ali, v splošnem, enakost dveh + stvari), uporabimo dvojni enačaj, ==. Enojni enačaj je dobil + svojo zadolžitev prejšnjo uro, namenjen je namreč prirejanju. Ali sta dve + stvari različni, preverimo z !=.

+ +
>>> 1 + 1 == 2
+True
+>>> 1 + 1 != 2
+False
+

+ +

Ne zamenjujte dvojnih in enojnih enačajev. Če pomotoma napišemo (a to se nam + bo rekdo zgodilo),

+
 a == 1 + 1
+

s tem nismo priredili a-ju dvojke, temveč smo le preverili, ali je + enak 2. Pogostejša napaka pa bo

+
 if a = 1:
+    print("Vrednost a je ena")
+

Ta program pa je sintaktično nepravilen, saj Python za if +pričakuje pogoj, mi pa smo napisali prireditveni stavek.

+ + + +

Zdaj pa Pythonova posebnost, ki je v drugih jezikih ne bo: operatorje smemo nizati. Izraz + 10 < T < 20 je resničen, če je T med 10 in + 20. Izraz 10 < T1 < T2 < 20 je resničen, če sta + T1 in T2 med 10 in 20, pri čemer je + T1 manjši od T2. Izraz + T1 < 10 < T2 je resničen, če je T1 manjši + od 10, T2 pa večji od deset. + 10 < T1 == T2 < 20 je resničen, če sta + T1 in T2 enaka in sta nekje med 10 in 20.

+ +

Če se torej končno vrnemo k suhcem in debeluhom: če bi se hoteli + skoncentrirati le na te, ki so ravno pravšnji, bi lahko napisali, + recimo

+ +
if bmi > 18.5 and bmi < 25:
+    print("Čestitamo, ravno pravšnji ste.")
+ +

Če kdo napiše

+ +
if (bmi > 18.5) and (bmi < 25):
+    print("Čestitamo, ravno pravšnji ste.")
+ +

ali celo

+ +
if ((bmi > 18.5) and (bmi < 25)):
+    print("Čestitamo, ravno pravšnji ste.")
+ +

Ga bomo čudno gledali (((kdor hoče oklepaje, naj gre raje programirat v +Lisp))). Prav po Pythonovsko pa se reče:

+ +
if 18.5 < bmi < 25:
+    print("Čestitamo, ravno pravšnji ste.")
+ + + + diff --git a/python/problems/while_and_if/ifclause_sl.html b/python/problems/while_and_if/ifclause_sl.html new file mode 100644 index 0000000..c1f9134 --- /dev/null +++ b/python/problems/while_and_if/ifclause_sl.html @@ -0,0 +1,330 @@ + + + + + + + + + + +

Pogojni stavki

+ +

Sledijo zapiski predavanj prof. Demšarja. Najnovejšo verzijo zapiskov dobite na + spletni + učilnici.

+ +

Začnimo s preprostim programom za izračun indeksa telesne teže.

+ +
teza = float(input("Teža: "))
+visina = float(input("Telesna višina: "))
+bmi = teza / visina ** 2
+print("Indeks vaše telesne teže je", bmi)
+ +

Želimo si, da bi program povedal še, ali je uporabnik slučajno + preobilen. Prvi, seveda napačni poskus, bi bil takšen.

+ +
teza = float(input("Teža: "))
+visina = float(input("Telesna višina: "))
+bmi = teza / visina ** 2
+print("Indeks vaše telesne teže je", bmi)
+print("Potrebno bo shujšati!")
+ +

Program v tej obliki bi, v širši rabi, povzročal anoreksijo, saj vse po +vrsti pošilja na hujšanje. Zadnjo vrstico, izpis poziva k hujšanju, mora izvesti +le, če je bmi večji od 25 (ta meja namreč velja za mejo debelosti, +oziroma, politično korektno, prekomerne telesne teže).

+ +

To storimo tako, da pred vrstico dodamo pogoj. Ta se glasi "če bmi > 25", +le v angleščini.

+ +
teza = float(input("Teža: "))
+visina = float(input("Telesna višina: "))
+bmi = teza / visina ** 2
+print("Indeks vaše telesne teže je", bmi)
+if bmi > 25:
+    print("Potrebno bo shujšati!")
+ +

Ne spreglejmo dveh pomembnih reči. Prva: na konec pogoja smo postavili + dvopičje. To ne služi le za okras: dvopičja ne smemo izpustiti. Python na + tem mestu zahteva dvopičje.

+ +

Druga: za if-om sem vrstico nekoliko zamaknil. Obilajno je zamik + štiri presledke. Ni obvezno, a če boste uporabljali štiri presledke, boste + pisali enako kodo kot drugi. In nikoli nikoli ne uporabljajte tabulatorjev, + ti naredijo zmedo!

+ +

Zamaknjenih vrstic bi lahko bilo še več. Vse se izvedejo le, če +je pogoj izpolnjen.

+ +
teza = float(input("Teža: "))
+visina = float(input("Telesna višina: "))
+bmi = teza / visina ** 2
+print("Indeks vaše telesne teže je", bmi)
+if bmi > 25:
+    print("Potrebno bo shujšati!")
+    print("Da, gospod ali gospa, preveč vas je skupaj.")
+print("Pregled je končan, oglasite se spet čez dva tedna.")
+ +

Tako kot smo zastavili zdaj, se printa, ki sta zamaknjena, +izvedeta le, če je oseba preobilna. Zadnji print ni več znotraj +pogoja, zato se izvede v vsakem primeru, za suhe in debele.

+ +

Program bi bil še bolj simpatičen, če bi tiste, ki niso predebeli, pohvalil. +

+ +
teza = float(input("Teža: "))
+visina = float(input("Telesna višina: "))
+bmi = teza / visina ** 2
+print("Indeks vaše telesne teže je", bmi)
+if bmi > 25:
+    print("Potrebno bo shujšati!")
+    print("Da, gospod ali gospa, preveč vas je skupaj.")
+else:
+    print("Odlično, le še naprej jejte toliko kot doslej!")
+print("Pregled je končan, oglasite se spet čez dva tedna.")
+ +

Pogojnemu stavku lahko sledi še + else, ki vsebuje tisto, kar naj se zgodi, če pogoj ni + izpolnjen.

+ +

Znotraj pogojnih stavkov seveda lahko pišemo tudi druge reči, ne le +printov. Takole: uporabniki programa se bodo pogosto zmotili in +namesto višine v metrih vpisali višino v centimetrih, zato jih bo program +obtožil težke anoreksije. To lahko popravimo preprosto tako, da program še pred +računanjem BMI preveri, ali je višina slučajno večja od, recimo, treh metrov. +V tem primeru sklepa, da je uporabnik podal višino v centimetrih, zato številko, +ki jo je vpisal uporabnik, deli s 100.

+ +

Začetek programa tako postane takšen.

+ +
teza = float(input("Teža: "))
+visina = float(input("Telesna višina: "))
+if visina > 3:
+    visina = visina / 100
+bmi = teza / visina ** 2
+ +

Še ena estetska zadeva: za dvopičjem vedno pojdite v novo vrsto. Kot boste + zvedavi kmalu odkrili, Python sicer dovoli, da napišete tudi +

if bmi > 25: print ("Potrebno bo shujšati")
+vendar to vodi v nepregledne programe. To se ne dela.

+ +

Tule pa je še nekaj primerov napačno zamikanih programov.

+ +
if bmi > 25:
+    print("Potrebno bo shujšati!")
+  print("Da, gospod ali gospa, preveč vas je skupaj.")
+else:
+    print("Odlično, le še naprej jejte toliko kot doslej!")
+print("Pregled je končan, oglasite se spet čez dva tedna.")
+ +

Tretja vrstica mora imeti enak zamik kot druga, sicer Python ne more vedeti, + ali je še znotraj pogoja ali ne.

+ +
if bmi > 25:
+    print("Potrebno bo shujšati!")
+      print("Da, gospod ali gospa, preveč vas je skupaj.")
+else:
+    print("Odlično, le še naprej jejte toliko kot doslej!")
+print("Pregled je končan, oglasite se spet čez dva tedna.")
+ +

Ne, tudi v to smer ne smemo. Tu se Python vpraša, zakaj je tretja vrstica + zamaknjena še bolj kot druga - smo pozabili še kakšen pogoj ali kaj?

+ +
if bmi > 25:
+print("Potrebno bo shujšati!")
+print("Da, gospod ali gospa, preveč vas je skupaj.")
+
+ +

Blok za stavkom if mora biti zamaknjen.

+ +
if bmi > 25:
+    print("Potrebno bo shujšati!")
+    print("Da, gospod ali gospa, preveč vas je skupaj.")
+  else:
+    print("Odlično, le še naprej jejte toliko kot doslej!")
+print("Pregled je končan, oglasite se spet čez dva tedna.")
+ +

if in else morata biti poravnana. Kmalu bomo videli +programe z več ifi in elsei; zamiki bodo potrebni, +da bomo vedeli, kateri else sodi h kateremu if.

+ +

Če poskušamo zagnati kateregakoli od naštetih programov, bo Python javil + sintaktično napako. Za razliko od običajnih napak, ki jih boste delali, in + ko bodo programi naredili vsaj nekaj malega in potem crknili, ta ne bo niti + trznil, temveč kar takoj javil napako. Spodnji program pa je sintaktično + pravilen. Python ga lahko izvede. Najbrž pa to ni to, kar smo hoteli, saj + se zadnji print izvede le, če pogoj ni izpolnjen.

+ +
if bmi > 25:
+    print("Potrebno bo shujšati!")
+  print("Da, gospod ali gospa, preveč vas je skupaj.")
+else:
+    print("Odlično, le še naprej jejte toliko kot doslej!")
+    print("Pregled je končan, oglasite se spet čez dva tedna.")
+ + +

Gnezdeni pogoji

+ +

Do sem vse lepo in prav, vendar: kaj bomo z anoreksičnimi? Program bi jim +mirno svetoval, naj še naprej jedo toliko kot doslej (torej: premalo). +Ne: pri takšnih, ki imajo BMI manjši od +18.5, moramo zastokati, naj vendar že začnejo jesti.

+ +
teza = float(input("Teža: "))
+visina = float(input("Telesna višina: "))
+bmi = teza / visina ** 2
+print("Indeks vaše telesne teže je", bmi)
+if bmi > 25:
+    print("Potrebno bo shujšati!")
+    print("Da, gospod ali gospa, preveč vas je skupaj.")
+else:
+    if bmi < 18.5:
+        print("Trlica!")
+        print("Zunaj so avtomati s sendviči in čokolado. Med pavzo si le postrezite!")
+    else:
+        print("Odlično, le še naprej jejte toliko kot doslej!")
+print("Pregled je končan, oglasite se spet čez dva tedna.")
+ +

Vidimo? Znotraj ifa ali elsea smemo +ugnezditi nov pogoj. Zamikamo ga lepo naprej.

+ +

Da bodo reči še nekoliko daljše (a poučnejše), dodajmo še en + print.

+ +
teza = float(input("Teža: "))
+visina = float(input("Telesna višina: "))
+bmi = teza / visina ** 2
+print("Indeks vaše telesne teže je", bmi)
+if bmi > 25:
+    print("Potrebno bo shujšati!")
+    print("Da, gospod ali gospa, preveč vas je skupaj.")
+else:
+    print("Predebeli ravno niste.")
+    if bmi < 18.5:
+        print("Trlica!")
+        print("Zunaj so avtomati s sendviči in čokolado. Med pavzo si le postrezite!")
+    else:
+        print("Odlično, le še naprej jejte toliko kot doslej!")
+print("Pregled je končan, oglasite se spet čez dva tedna.")
+ +

Zdaj program vsem, ki niso predebeli, napiše dobro novico, da niso predebeli. +Šele nato se loti preverjanja, da niso slučajno presuhi.

+ +

Tule mimogrede (prvič - a še velikokrat bomo) posvarimo pred ponavljanjem +kode. Gornji program bi lahko napisali tudi tako:

+ +
teza = float(input("Teža: "))
+visina = float(input("Telesna višina: "))
+bmi = teza / visina ** 2
+print("Indeks vaše telesne teže je", bmi)
+if bmi > 25:
+    print("Potrebno bo shujšati!")
+    print("Da, gospod ali gospa, preveč vas je skupaj.")
+else:
+    if bmi < 18.5:
+        print("Predebeli ravno niste.")
+        print("Trlica!")
+        print("Zunaj so avtomati s sendviči in čokolado. Med pavzo si le postrezite!")
+    else:
+        print("Predebeli ravno niste.")
+        print("Odlično, le še naprej jejte toliko kot doslej!")
+print("Pregled je končan, oglasite se spet čez dva tedna.")
+ +

Ali celo tako.

+ +
teza = float(input("Teža: "))
+visina = float(input("Telesna višina: "))
+bmi = teza / visina ** 2
+print("Indeks vaše telesne teže je", bmi)
+if bmi > 25:
+    print("Potrebno bo shujšati!")
+    print("Da, gospod ali gospa, preveč vas je skupaj.")
+    print("Pregled je končan, oglasite se spet čez dva tedna.")
+else:
+    if bmi < 18.5:
+        print("Predebeli ravno niste.")
+        print("Trlica!")
+        print("Zunaj so avtomati s sendviči in čokolado. Med pavzo si le postrezite!")
+        print("Pregled je končan, oglasite se spet čez dva tedna.")
+    else:
+        print("Predebeli ravno niste.")
+        print("Odlično, le še naprej jejte toliko kot doslej!")
+        print("Pregled je končan, oglasite se spet čez dva tedna.")
+ +

To ni dobra ideja, ker je program daljši, manj pregleden, težje mu je + slediti, težje ga je spreminjati... Vedno se izogibajte ponavljanju. Več + o tem pa čez dva ali tri tedne.

+ +

Sicerče

+ +

Da se ne izgubimo, spet malo skrajšajmo program.

+ +
teza = float(input("Teža: "))
+visina = float(input("Telesna višina: "))
+bmi = teza / visina ** 2
+print("Indeks vaše telesne teže je", bmi)
+if bmi > 25:
+    print("Potrebno bo shujšati!")
+else:
+    if bmi < 18.5:
+        print("Trlica!")
+    else:
+        print("Odlično, le še naprej jejte toliko kot doslej!")
+ +

Pogojni stavki se nam lahko hitro zagnezdijo precej globoko in vrstice +zbežijo nekam daleč proti desni. (Da ne govorimo o tem, da pogojni stavki +niso niti približno edina reč, za katero bomo morali zamikati vrstice.) Ker +so situacija, kakršna je zgornja, kar pogoste, imajo nekateri programski jeziki +- in Python je med njimi - poleg if in else še +elseif oziroma elif. (Še ena rezervirana beseda!) +V Pythonu se uporablja slednja različica, elif. V gornjem programu +ga uporabimo takole: + +

teza = float(input("Teža: "))
+visina = float(input("Telesna višina: "))
+bmi = teza / visina ** 2
+print("Indeks vaše telesne teže je", bmi)
+if bmi > 25:
+    print("Potrebno bo shujšati!")
+elif bmi < 18.5:
+    print("Trlica!")
+else:
+    print("Odlično, le še naprej jejte toliko kot doslej!")
+ +

Program se zdaj prebere še lepše kot prej: če je BMI prevelik, pozovemo + k hujšanju, sicerče je premajhen, ozmerjamo dotično najstnico s trlico, + sicer pa izrečemo pohvalo.

+ +

Po izkušnjah je elif za študente huda, nenavadna skušnjava: + nekateri ga pogosto uporabljajo namesto elsea. Recimo, da bi + hoteli sprogramirati skrajšano pesimistično različico, po kateri so vsi, + ki niso debeluhi, trlice. Radi bi torej tole:

+
if bmi > 25:
+    print("Potrebno bo shujšati!")
+else:
+    print("Trlica!")
+
+ +

Mnogi bi (iz meni neznanega razloga) naredili takole:

+ +
if bmi > 25:
+    print("Potrebno bo shujšati!")
+elif bmi < 25:
+    print("Trlica!")
+
+ +

Python ni pozabil ifa in bo tudi, če napišemo le + else, čisto dobro vedel, kakšen je pogoj zanj. Drugi pogoj + je nepotreben; če bmi ni večji od 25, je pač očitno manjši.

+ +

Torej: ne zamenjujte else brez potrebe z elif.

+ +

Mimogrede, program ne izpiše ničesar za tiste, ki imajo BMI natančno +25. Drugi pogoj, če ga res hočemo imeti, bi se moral vprašati, ali je +bmi manjši ali enak 25.

+ + + + + diff --git a/python/problems/while_and_if/loops_sl.html b/python/problems/while_and_if/loops_sl.html new file mode 100644 index 0000000..17d89c4 --- /dev/null +++ b/python/problems/while_and_if/loops_sl.html @@ -0,0 +1,179 @@ + + + + + + + + + + +

Zanka while

+ +

Naša prva zanka

+ +

Najprej malo refleksije. Kaj znamo narediti doslej: znamo napisati program, + ki kaj vpraša, kaj računa in kaj izpiše. Naši programi se izvajajo od + začetka do konca, s tem da lahko vmes preskočijo kak blok ukazov, ki se + nahajajo znotraj kakega if, else ali + elif. Večino časa pa smo preživeli ob tem, kako napisati + pogoj, se pravi, izraz, katerega vrednost je lahko resnično ali neresnično, + True ali False. Zdaj pa se bomo naučili napisati + programe, v katerih se del programa ponavlja.

+ +

V večini programskih jezikov to naredimo z nečim, čemur mi rečemo zanka, + Avstralci (razen aboriginov) pa loop. Jeziki imajo navadno več + vrst zank, navadno tri. Python premore samo dve in danes bomo spoznali prvo + od njiju: zanko while.

+ +

Tisočletna tradicija veleva, da mora biti začetnikov prvi program z zanko + program, ki šteje do deset. V Pythonu je videti takole +

x = 1
+while x <= 10:
+    print(x)
+    x = x + 1
+print('Pa sva preštela')
+ +

Zanka while je po svoje podobna stavku if. Medtem + ko se stavki znotraj if izvedejo, če je pogoj + resničen, se stavki znotraj while ponavljajo, + dokler je pogoj resničen. "If" je "če" in "while" je "dokler", to + vedo celo učiteljice angleščine. (Mimogrede, če že štejemo, + while je zadnja, enajsta rezervirana beseda danes. Smo že na + tretjini!) Gornji program lahko torej kar dobesedno prevedemo iz Pythona v + slovenščino: + +

+postavi x na 1
+dokler je x manjši ali enak 10:
+    izpiši vrednost x
+    povečaj x za 1
+izpiši 'Pa sva preštela'
+
+ +

K temu ni potrebna več nobena razlaga, ne?

+ +

Trenutek je tako dober kot katerikoli drugi, morda pa še boljši: tale + x = x + 1 smo "prevedli" v "povečaj x za 1". V resnici nismo + napisali tega, računalniku smo naročili, naj izračuna, koliko je + x + 1 in to shrani nazaj v x. Ker pa to reč + tolikokrat potrebujemo in ker je lepo, da se stvar napiše tako, kot se + misli - misli pa se "povečaj x za 1" -, obstaja tudi praktičnejši zapis: + x += 1. Točneje, napisali smo "k x prištej 1". Rečemo lahko + tudi, x += 6 ali x += 1.13; kaj to pomeni, je + menda jasno. Podobno moremo pisati tudi x -= 1 (zmanjšaj + x za 1) ali, recimo x *= 2, podvoji + x in x /= 10, zdesetkaj x. + Pazite: += (in vse druge kombinacije) se štejejo kot ena + beseda in med znaka + in = ne smemo napisati + presledka.

+ +

Tisti, ki veste še kaj več: x++ boste v Pythonu zaman iskali. + Nima ga, iz več razlogov, ki pa so malo pregloboki za nas tule.

+ +

Kaj je domneval Collatz

+ +

Vzemimo poljubno število in z njim počnimo tole: če je sodo, ga delimo z 2, +če je liho, pa ga pomnožimo s 3 in prištejmo 1. To ponavljamo, dokler ne dobimo +1.

+ +

Za primer vzemimo 12. Ker je sodo, ga delimo z 2 in dobimo 6. 6 je sodo, +torej ga delimo z 2 in dobimo 3. 3 je liho, torej ga množimo s 3 in prištejemo +1 - rezultat je 10. 10 je sodo, zato ga delimo z 2 in dobimo 5... Celotno +zaporedje je 12, 6, 3, 10, 5, 16, 8, 4, 2, 1. Ko pridemo do 1, se ustavimo.

+ +

Matematiki, ki vedno radi počnejo koristne reči, si belijo glavo z +vprašanjem, ali se reč vedno izteče ali pa se lahko kdaj tudi zacikla tako, da +se zaporedje ponavlja in ponavlja v nedogled. Lothar Collatz, konkretno je že od +leta 1937 +domneval, da se +zaporedje vedno izteče. Kot pravi njegov +življenjepis, se je +njegovo domnevanje končalo leta 1990, matematiki pa domnevajo domnevo še naprej. +Eden sicer najbolj neustrašnih matematikov 20. stoletja, +couch + surfer Erdos, je na to temo rekel, da matematika za takšna vprašanja še + ni zrela.

+ +

Naši cilji bodo skromnejši: napisali bomo program, ki mu bo uporabnik vpisal +število in program bo izpisal zaporedje, kot je bilo gornje.

+ +

Najprej povejmo kar po slovensko:

+ +
stevilo = kar vpiše uporabnik
+dokler stevilo != 1:
+    če je število sodo:
+        deli število z 2
+    sicer:
+        pomnoži število s 3 in prištej 1
+ +

Tole skoraj že znamo prevesti v Python (prevod bo pravzaprav dobeseden), + odkriti moramo le, kako preveriti, ali je število sodo. Če kdo misli, da + tudi za to obstaja funkcija, se, izjemoma, moti. Pač pa znamo izračunati + ostanek po deljenju, konkretno, ostanek po deljenju z 2. Če je enak 0, je + število sodo.

+ +

Prevedimo torej one slovenske besede v pythonovske.

+ +
n = int(input("Vnesi število: "))
+while n != 1:
+    print(n)
+    if n % 2 == 0:
+        n //= 2
+    else:
+        n = n * 3 + 1
+print(1)
+ +

Ne spreglejte, da smo uporabili celoštevilsko deljenje. Če bi uporabili +navadnega, n /= 2, bi se n spremenil v necelo število +(float) in v izpisu bi se pojavile zoprne decimalke (za vsakim +številom bi pisalo še .0).

+ +

Popaziti je potrebno, da izpišemo tako prvo kot zadnje število, zato bomo +potrebovali še en print izven zanke: po zanki izpišemo še zadnji +člen zaporedja, 1. Namesto print(1) bi lahko napisali tudi +print(n) - tako ali tako vemo, da je n enak 1, saj +program sicer ne bi prišel do tam, do koder je prišel, namreč onstran zanke, ki +teče, dokler je n različen od 1.

+ +

Dodajmo še nekaj: na koncu naj se izpiše, koliko členov ima zaporedje.

+ +
n = int(input("Vnesi število: "))
+clenov = 1
+while n != 1:
+    clenov += 1
+    print(n)
+    if n % 2 == 0:
+        n //= 2
+    else:
+        n = n * 3 + 1
+print(1)
+print("Zaporedje, ki se začne z", n, "ima", clenov, "členov")
+ +

Ups. V zanki smo pokvarili n, spremenil smo ga v 1. To bomo +rešili tako, da shranimo začetno število v še eno spremenljivko.

+ +
zac = n = int(input("Vnesi število: "))
+clenov = 1
+while n != 1:
+    clenov += 1
+    print(n)
+    if n % 2 == 0:
+        n //= 2
+    else:
+        n = n * 3 + 1
+print(1)
+print("Zaporedje, ki se začne z", zac, "ima", clenov, "členov")
+ +

Število smo si zapomnili v prvi vrstici, shranili smo ga v +zac. (V tem programu tudi prvič vidimo, da lahko prirejanja kar +nizamo - pisati smemo a = b = c = 42.) Ustrezno popravimo še +izpis v zadnji vrsti.

+ + + + diff --git a/python/problems/while_and_if/sl.py b/python/problems/while_and_if/sl.py index fbb177a..7caffb4 100644 --- a/python/problems/while_and_if/sl.py +++ b/python/problems/while_and_if/sl.py @@ -1,2 +1,4 @@ name = 'Pogojni stavki in zanke' -description = 'Logični izrazi, pogojni stavek if-else, zanka while.' +description = '''Pogojni stavek if-else, +logični izrazi +zanka while''' -- cgit v1.2.1