Čo objaví Pascalista v Pythone
description
Transcript of Čo objaví Pascalista v Pythone
Čo objaví Pascalista v Pythone
Peter Borovanský, KAI, I-18, borovan(a)ii.fmph.uniba.sk
Cieľom prednášky: je ukázať zaujímavé jazykové konštrukcie jazyka Python
(procedurálny, objektový a funkcionálny štýl, list-comprehension, generátory, ...)
neurobiť ďalší tutorial, ani prehľad metód akejkoľvek triedy skúsenosti s jazykom (čo ma zaujalo a prekvapilo)
Literatúra: Python docs http://docs.python.org/py3k/ Python in Education http://www.python.org/community/sigs/current/edu-sig/ Learning with Python (2.x) http://openbookproject.net//thinkCSpy Programovací jazyk Python http://www.py.cz (http://howto.py.cz/index.htm )
Cvičenie: malá pythoniáda - jednoduché pythonovské programy interpreter konečného automatu/Turingového/Minského stroja
Guido van Rossum v roku 1996 napísal:
"Pred šiestimi rokmi, v decembri 1989, som hľadal zábavný programátorský projekt, ktorý by ma zamestnal cez týždeň počas Vianoc. Moja kancelária … bola zavretá, ale mal som domáci počítač a nič iného na práci. Rozhodol som sa, že napíšem interpreter pre nový skriptovací jazyk, o ktorom som už skôr premýšľal: nasledovníka jazyka ABC, ktorý by zaujal programátorov v Unixe/C. Ako pracovný názov som zvolil Python, lebo som bol v nevážnej nálade (a tiež som veľkým fanúšikom Monty Pythonovho Lietajúceho cirkusu)."
http://sk.wikipedia.org/wiki/Guido_van_Rossum
Jednoduché typy interpretovaný jazyk poskytuje typy podobné int, long, float, complex, bool, str(ing), … implicitné číselné konverzie až na konverziu z/do string (1j*1j+1 == 0) dynamicky typovaný jazyk
každá hodnota je asociovaná s typom, avšak typ premennej/konštrukcie nie je známy počas kompilácie, ale je známy až v čase výpočtu
def dynatest(flag): if flag: var = 1234 else: var = "retazec" print(var,':',type(var)) if flag: var = 3.4 else: var = flag print(var,':', type(var))
dynatest(True)dynatest(False
)1234 : <class 'int'>3.4 : <class 'float'>retazec : <class 'str'>False : <class 'bool'>
Analógia null a void z C++None : <class 'NoneType'>
Rozdiel medzi dynamicky a staticky typovaným jazykom
Operácie nad základnými typmi
pozri si poriadne http://docs.python.org/library/stdtypes.htmlani Pascal ani C...
Pascal: x and y, x or y, not z, True, False predsa pozná <> ako
anachronizmus int(x), float(x), long(x), str(x) sú
typové konverzie à la Pascal
C: case sensitive, True != true ==, !=, = ((ne)rovnosť,
priradenie) +, -, *, /, //, %, ** sú operátory
sčítania, ... , celočíselné delenie, modulo, umocnenie
nepozná i++, --i, len i += 1, i -= 1 bitové operácie |, &,^,~,>>,<< indexujeme C-éčkovsky, od 0 po
size-1
Nápady tretích strán: divmod(x,y) vráti dvojicu (div,mod) math.ceil(x), math.floor(x) à la Java
...toto sme nechceli robiť, ale niečo z toho môže byť na rozcvičke
Kolekcie - sekvencie heterogénne zoznamy (môžu obsahovať objekty rôznych typov)
[1,2,3] – trojprvkový zoznam s prvkami 1,2,3 [] – prázdny zoznam [[1,2,3]] – jednoprvkový zoznam, ktorého jediný prvok je trojprovkový
zoznam [1,3.1415,['a','b']] : <class 'list'> - typ zoznamu
reťazce – "retazec", 'string' : <class 'str'> n-tice - (True, 3.1415, "retazec", [1,2,3,4]), (), (1,) : <class 'tuple'> for-cyklus cez sekvencie:
for elem in sekvencia: # elem je premenná prebiehajúca sekvenciou …for e in [1,3.1415,['a','b']]: for chr in "retazec": print(e,':',type(e)) print(chr,’:’,type(chr))1 : <class 'int'> r : <class 'str'>3.1415 : <class 'float'> e : <class 'str'>['a', 'b'] : <class 'list'> t : <class 'str'> …
analógia kolekcií napr. z Java
Operácie so sekvenciami(najbežnejšie) x in s, x not in snachádza/nenachádza sa v sekvencii len(s) dĺžka sekvencie min(s), max(s) minimálny/maximálny prvok s.index(i) prvý výskyt i v sekvencii s, inak -1 s.count(i) počet výskytov i v sekvencii s s + t zreťazenie dvoch sekvencií s * n, n * s s+s+s+…+s a to presne n-krát s[i] i-ty prvok s s[i:j] podsekvencia s[i,i+1,…,j-1] s[i:j:k] podsekvencia s[i,i+k,…,???]
4 in [0,1,2,3,4,5] True4 not in range(6) Falselen(range(10)) 10max(range(10)) 9[0]*10 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0](0,)*3 (0, 0, 0)
Príklady:'t' in 'retazec‘ True't' not in 'retazec‘ Falselen('retazec') 7min('retazec') 'a''retazec'.index('t')2'r'*10 'rrrrrrrrrr'2*'r'*3 'rrrrrr'
Indexovaniesekvencia[indexOdVrátane: indexPo: krok]ktorýkoľvek z parametrov môžeme vynechať:
"retazec"[3] == 'a'"retazec"[3:] == 'azec'"retazec"[3::2] == 'ae'"retazec"[:5] == 'retaz'"retazec"[-1] == 'c'"retazec"[::-1] == 'cezater'"retazec"[-1:-6:-2] == 'czt‘
reťazce'retazec'.upper() == 'RETAZEC''stav,pismenko,novepismenko,novystav'.split(',') == ['stav', 'pismenko', 'novepismenko', 'novystav']': '.join(["janko","marienka"])== 'janko:marienka'
Zložitejšie kolekcie – slovník
slovníkový typ s kľúčom Key a hodnotou Value je zobrazenie Key→Value
Príklad: ilustrujeme slovník typu str → int, aj keď slovník tiež môže byť hetero
vytvorenie slovníkaslovnik = dict({"janko":1, "marienka":2, "jezibaba":10}) : <class 'dict'>
indexovanie kľúčom: slovnik["marienka"]
modifikácia: slovnik["janko"]=99
odstránenie prvku: del slovnik["jezibaba"]
tlač: print(slovnik) {'marienka': 2, 'janko': 99}
cyklus cez kolekciu:for k,v in slovnik.items():
print(k,v)
v jave Map<K,V>
Multiparadigmový(štruktúrovane) unikátnosť: indetovanie (odsadenie začiatku riadku od ľavého
okraja) je dôležité a nahrádza blok {…}/begin…end riadiace príkazy (if…elif…elif…else, for, while, break a
continue) nepozná repeat…until/do…while, ani case/switch má výnimky (try…except…except…except:)
dovolí programovať:
•procedurálne (štruktúrovane),•objektovo,•funkcionálne
if cond: ...
elif cond: ...
else: …
while cond:
...
for var in sekvencia:
...# funnySort zoznam= [4,3,2,5,3,1,2,4,6,8,9,2]size = len(zoznam)for i in range(size): # i = 0, 1, …, size-1 for j in range(1, size): # j = 1, …, size-1 if zoznam[j] < zoznam[j-1]: zoznam[j-1], zoznam[j] = zoznam[j], zoznam[j-1] # paralelné :=
Prémia Ackerman
from collections import deque # z modulu collection zober triedu dequedef acker(m, n): """ riešenie ... - dokumentačný reťazec, ktorý sa patrí napísať """ parametre, vysledky = deque(), deque() # dva zasobníky na držanie
stavu parametre.append( (m, n,) ) # vloženie (m, n) do zásobníka while len(parametre): m, n = parametre.pop() # vyber (m, n) zo zásobníka if n is None: n = vysledky.pop() # tretie pravidlo – časť 2 if m == 0: # prvé pravidlo vysledky.append(n + 1) continue if n == 0: # druhé pravidlo parametre.append( (m - 1, 1,) ) continue parametre.append( (m - 1, None,) ) # tretie pravidlo – časť 1 parametre.append( (m, n - 1,) ) return vysledky.pop()
def rekurzivny(m, n): if m == 0: return n + 1 if n == 0: return rekurzivny(m - 1, 1) return rekurzivny(m - 1, rekurzivny(m, n - 1))
riešenie anonymného študenta
S jedným zásobníkom
def ackermann(m,n):""" idea: zasobnik [zvysok,m,n<-] sa časom zmení na [zvysok,A(m,n)<-] """ stack = [m,n,] while len(stack) > 1: # pokiaľ sú na zásobníku 2 argumenty n = stack.pop() # vyber ich ... m = stack.pop() # ... v správnom poradí if m == 0: # [zvysok,0,n<-] => [zvysok,n+1<-] stack.append(n+1) elif m > 0 and n > 0: # [zvysok,m,n<-] => ([zvysok,m-1,m,n-1<-] stack.extend([m-1,m,n-1]) else: # [zvysok,m,0<-] => [zvysok,m-1,1<-] stack.extend([m-1,1]) return stack.pop() # na zásobníku zostal už len výsledok
nm
…
A(m,n)
…
http://en.wikipedia.org/wiki/Ackermann_function
Neštruktúrovane(goto in Python)
goto a comefrom boli pridané ako rozšírenia importované z modulu goto (1.apríla 2004)
from goto import goto, label for i in range(1, 10): for j in range(1, 20): for k in range(1, 30): print i, j, k if k == 3: goto .end label .end print "Finished\n"
from goto import comefrom, label def bigFunction(): setUp() if not doFirstTask(): label .failed if not doSecondTask(): label .failed if not doThirdTask(): label .failed comefrom .failed cleanUp()
http://mail.python.org/pipermail/python-announce-list/2004-April/002982.html
Objektovo triedy, objekty, preťažovanie operátorov dedenie (aj viacnásobne)
Definícia triedy a konštruktora:class BinTreeNode: """ definícia triedy s lokálnymi (triednymi) premennými """ left, right, value = None, None, None
def __init__(self, left, value, right): # jediný konštruktor BinTreeNode self.value = value # volanie konštruktora self.left = left # tree = BinTreeNode(None,5,None) self.right = rightprvý argument metódy je v definícii explicitne self, t.j. objekt (self), na ktorý sa metóda
aplikuje pri volaní implicitne objekt (self), na ktorý sa metóda aplikuje
Stratila sa idea C,Java…, že funkcie/procedúry/metódy sa definujúrovnako, ako sa aplikujú. Tu sa dokonca volajú inak...
Preťažovanie operátorovNiektoré operátory sú naviazané na metódy s preddefinovanými
menami tvaru __xyz__ a tie môžeme predefinovať, napr.: def __str__(self):# textová reprezentácia objektu self
return str(self.value)+"("+str(self.left)+","+str(self.right)+")"Vyhľadávanie v binárnom vyváženom strome: def __contains__(self, elem): # elem in self if self.value == elem:# deep compare – porovná celé štruktúry return True# našiel sa elif self.value < elem:# musí byť v pravo if self.right == None: # ak vpravo nie je nič return False else: return elem in self.right # rekurzia vpravo else: # musí byť vľavo if self.left == None: # ak vľavo nie je nič return False else: return elem in self.left # rekurzia vľavo
Duck typingje forma dynamického typovania, dynamická náhrada virtuálnych metód
class pes(): # definujeme dve triedy bez akejkoľvek defičnosti def zvuk(self): # obe definujú metódu zvuk() return "haw-haw" class macka(): def zvuk(self): return "mnau-mnau"
def zvuk(zviera): # otázkou (statického programátora) je, akého typu je print(zviera.zvuk()) # premenná zviera, keď na ňu aplikujeme .zvuk() # odpoveď: uvidí sa v RT podľa hodnoty premennejfarma = [pes(), macka()] # heterogénny zoznam objektov
for zviera in farma: zvuk(zviera)
If it looks like a duck and quacks like a duck, it must be a duck
haw-hawmnau-mnau
pes macka
Ako by to pascalista s dedičnosťouclass animal(): # nadtrieda def zvuk(self): # náznak virtuálnej metódy return "no sound" # neviem aký zvuk, resp. pass
class dog(animal): # dog ako podtrieda animal def zvuk(self): # override metódy zvuk z animal
return "haw-haw" class cat(animal): # cat ako podtrieda animal def zvuk(self): # override metódy zvuk z animal return "mnau-mnau"
class cow(animal): # cow ako podtrieda animal pass # nemá vlastnú metódu zvuk
# pass znamená prázdny príkazfor animal in [dog(), cat(), cow()] : print(animal.zvuk())
haw-hawmnau-mnauno sound
dog cat cow
animal
List comprehension(množinová notácia)
V matematike definujeme množinu: M1 = { 2x | x ε [1;100) }M2 = { x2 | x ε [1;100), 2|x }M3 = { (x, x2) | x ε [1;10) }M4 = { {1..n} | n ε [1;10) }=
{ {1}, {1,2}, {1,2,3},… {1,2,3,4,…,9} }
M5 = { (a,b,c) | a ε [1;n), b ε [1;n), c ε [1;n), a+b+c<=n, a2+b2=c2 }""" pythagorejske trojuholniky s obvodom najviac n """
[(a,b,c) for a in range(1,n) for b in range(1,n) for c in range(1,n) if a+b+c <= n if a*a + b*b == c*c ]
Táto téma sa ešte objaví v Haskelli
V Pythone definujeme zoznam:[2*x for x in range(1,100)][x*x for x in range(1,100) if x
%2==0][(x,x*x) for x in range(1,10)][ [i for i in range(1,n+1)]
for n in range(1,10)]
[(3, 4, 5), (4, 3, 5), (5, 12, 13), (6, 8, 10), (7, 24, 25), (8, 6, 10), (8, 15, 17), (9, 12, 15), (9, 40, 41), (10, 24, 26),…
Použitie list-comprehension(príklad Quicksort)list-comprehension je veľmi expresívna syntaktická konštrukcia, keďže
v (matematikovi) blízkej notácii ukrýva jeden, resp. viacero vnorených cyklov s podmienkami. Dá sa bez nej žiť, avšak dostaneme rozsiahlejší a textovo menej prehľadný kód, ktorý používa rekurzívne/cyklické procedúry namiesto list-comprehension. Preto ju používajte ... ;-)
Príklad (elegantného použitia list-comprehension):def quicksort(L): if len(L) <= 1: return L else: pivot = L[0] # prvý prvok triedeného zoznamu bude
pivot ostatne = L[1:] # zvyšné triedené prvky okrem pivota return quicksort([x for x in ostatne if x<pivot])+\ # lilliputs [pivot]+\ # pivot quicksort([x for x in ostatne if x >= pivot]) # maxiputs
print(quicksort([4,3,2,5,7,4,5,6,3,1,2,3,5]))
FunkcionálnePython má lambda konštrukciu: dovolí definovať anonymné funkcie
v tvare lambda <argumenty>:<telo>, napr. lambda x : x*x, t.j. x→x2
Je anonymná podoba pomenovanej funkciedef square(x): return x*xRozdieľ (pre Pascalistu) spočíva v tom, že funkcie môžeme tvoriť v
run-time, dynamicky, teda ich môžeme vytvoriť ľubovoľný počet...
Funkcia je regulárna hodnota v jazyku, ktorú môžeme napr. aplikovať (na argumenty)
(lambda x:x*x)(5)25
mapovať (aplikovať fciu na každý element sekvencie)list(map(lambda n : n*n, [1,2,3,4,5,6,7,8,9,10]))[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
filtrovať (vybrať zo sekvencie len prvky spĺňajúce daný predikát)list(filter(lambda n: n%2>0, [1,2,3,4,5,6,7,8,9,10]))[1, 3, 5, 7, 9]
Python je inšpirovaný funkciami a funkcionálmi à la Haskell
map & filter(príklady)
list(map(f,data)) znamená [f(x) for x in data] symetrie matice reprezentovanej ako zoznam riadkovmatrix[::-1] [ [7,8,9], [4,5,6], [1,2,3] ]list(map(lambda row:row[::-1],matrix)) [ [3,2,1], [6,5,4], [9,8,7] ]
list(filter(p,data)) == [x for x in data if p(x)]prvočíslalist( filter(
(lambda n:0== sum( (1 for i in range(2,n) if n % i == 0)
) ), range(2,100) ))
matrix = [ [1,2,3],
[4,5,6], [7,8,9] ]
list(map(lambda i: n%i, range(2,n))).count(0),
Closures(len pre fajnšmeckerov)def addN(n): # výsledkom addN je funkcia,
return (lambda x:n+x) # ktorá k argumentu pripočína N
add5 = addN(5) # toto je jedna funkcia x→5+x add1 = addN(1) # toto je iná funkcia y→1+y
# … môžem ich vyrobiť neobmedzene veľaprint(add5(10)) # 15print(add1(10)) # 11
def iteruj(n,f): # výsledkom je funkcia fn
if n == 0: return (lambda x:x) # identita else: return(lambda x:f(iteruj(n-1,f)(x))) # f(fn-1) = fn
add5SevenTimes = iteruj(7,add5) # +5(+5(+5(+5(+5(+5(+5(100)))))))
print(add5SevenTimes(100)) # 135
Generátory(coroutiny)
Generátor je procedúra/funkcia, ktorá má v istom bode prerušený (a odložený) zvyšok svojho výpočtu. Generátor odovzdáva výsledky volajúcej procedúre pomocou príkazu
yield hodnota. Na obnovenie výpočtu (a pokračovanie v ňom) generátora slúži funkcia
next(gener)
def gen(n): # generátor generuje postupne čísla 0,1,2,...,n-1 for i in range(n):# cyklus pre i z intervalu yield i # yield vždy preruší výpočet cyklu a urobí return i print([x*x for x in gen(5)]) # for cyklus beží nad generátorom, [0,1,4,9,16]print(sum(gen(5))) # agregátor sum nad generátorom 10print(list(gen(5)) # list nad generátorom pozbiera jeho výsledkyg = gen(5)print(next(g)),print(next(g)),print(next(g)),print(next(g)),print(next(g)),print(nex
t(g)),…0 1 2 3 4 Exception
Python sa snaží byť lenivýAle skutočná lenivosť príde až Haskellom
Nekonečné generátorydef integers(n): # generuje nekonečne veľa výsledkov tvaru while True: # n, n+1, n+2, … yield n n += 1
print(list(integers(1))) # toto nemôže nikdy vytvoriť celý zoznamprint(min(integers(1))) # hoc minimum je zrejmé, ani toto
nedobehne[n*2 for n in integers(1)] # tu by už Haskell niečo dal, ale Python
nie ...
def take(n,g): # zober prvých n generovaných hodnôt gen. g for i in range(n): yield next(g) # next(g) vyprovokuje výpočet ďalšej hodnoty g
print(list(take(10,integers(1)))) # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Eratostendef sieve(d, sieved): # osievací generátor for x in sieved: # z generátora sieved prepúšťa len if (x % d != 0): # hodnoty nedeliteľné d yield x
def eratosten(ints): # eratostenovo sito (prvočísla :-) while True: # zober generátor ints=integers(2) first = next(ints) # prvé číslo predstavuje prvočíslo yield first # toto sa reportuje výsledok eratosten ints = sieve(first, ints) # preosejeme čísla tak, že
vyhádžeme# všetky deliteľné týmto prvočíslom # a pokračujeme v cykle
print(list(take(100,eratosten(integers(2)))))
s nekonečnom sa dá pracovať len lenivo
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541]
Fibonaccidef zip(gen1, gen2): # zipovač dvoch nekonečných generátorov while True: # do nekonečna pýtaj výsledok gen1 a gen2 yield next(gen1), next(gen2) # a vráť dvojicu týchto výsledkov ako # výsledok generatora zipprint(list(take(10,zip(integers(1), integers(2)))))
def tail(gen): # tento generátor vracia hodnoty rovnaké next(gen) # gen, akurát prvý výsledok zahodí, ignoruje... while True: # potom už vráti, to čo od gen dostane yield next(gen)
def fib(): # netradičná generátorová definícia Fibonacciho čísel yield 1 # prvý prvok postupnosti yield 1 # druhý prvok postupnosti for (x,y) in zip(fib(),tail(fib())):# zozipujeme fib a fib okrem prvého prvku yield x+y # z každej dvojice vyrobíme súčet a ten
# prezentujeme ako ďalšie fib čísloprint(list(take(20,fib())))
exec & evalexec reťazec - je príkaz, ktorý z reťazca vykoná príkaz v ňom
uloženýeval(reťazec) - je funkcia, ktorá z reťazca vyhodnotí výraz
Táto funkcionalita sa podľa očakávania nachádza v interpreteri.
>>>exec('a=1; print("hodnota a=",a)')# hurá, pascalovská ; je tu späť
hodnota a= 1>>>eval('a*a+a')2