Python ni imel izpeljanih seznamov od vedno. Dobil jih je, ko je imel okrog 9 let, večine študentov pa še ni bilo med nami. Z verzijo 2.0, leta 2000.
Predtem sta tej sceni vladali dve funkciji: map in
filter.
Funkcija map kot argument prejme funkcijo in nekaj, prek
česar je možno nagnati zanko. Vsak element tega, nečesa, "premapira" čez
funkcijo. Če imamo
from math import sqrt
k = [9, 25, 16, 81]bo map(sqrt, k) vrnil korene vseh števil v
k:
for x in map(sqrt, k):
print(x)Funkcija map dela, približno tole:
def map(func, s):
t = []
for x in s:
t.append(func(x))
return tDo Pythona 3.0 je funkcija map v resnici vračala seznam,
od različica 3.0 naprej pa vrne iterator. Za tiste, ki ne
veste, kaj je to: vede se kot seznam, samo da ni; čezenj lahko gremo z
zanko for. Za tiste, ki ne veste, pa bi radi izvedeli:
preberite zapiske o generatorjih in iteratorjih. Za tiste, ki veste: ja,
takšen:
def map(func, s):
for x in s:
yield func(s)Funkcijo map od Pythona 2.0 naprej uporabljamo zelo
redko. map(func, s) je isto kot
(func(x) for x in s). Prednost novejše različice je v tem,
da
(x ** 2 for x in s) ne moremo prepisati v
map(**2, s), temveč potrebujemo lambdo:
map(lambda x: x ** 2, s);map počasnejši, ker vedno kliče funkcijo, medtem ko
je novejši zapis, generator, ne (vsak, dokler lahko vse opravimo z
izrazom).Osebno map rad uporabim, kadar imam funkcijo ravno pri
roki in kadar izgleda sintaktično lepše.
Se pravi: redko.
Funkcija filter je druga funkcija, ki so jo izpeljani
seznami spravili ob delo. filter(func, s) vrne vse tiste
elemente s, pri katerih func vrne
True.
def vsebuje_i(s):
return "i" in s
imena = ["Ana", "Berta", "Cilka", "Dani", "Ema"]
for x in filter(vsebuje_i, imena):
print(x)To je seveda isto kot
(x for x in imena if vsebuje_i(x)), kar je tako ali tako le
bolj zapletena različica (x for x in imena if "i" in x).
Resnici na ljubo tudi filter ne potrebuje poprej definirane
funkcije, saj bi lahko pisali
filter(lambda x: "i" in x, imena). Vendar je očitno, zakaj
filtra ne vidimo več velikokrat.
Izpeljani seznami, slovarji, množice in generatorji v enem zamahu naredijo oboje, mapirajo in filtrirajo.
Funkcija reduce je edina iz te družbe, ki ni ostala
brezposelna. No, hkrati pa tudi najmanj uporabna od njih, saj Python ni
ravno jezik za te hece. Mogoče je tudi to razlog, da jo dobimo v modulu
functools in ne kar tako, na prostem.
reduce(func, s) je nekako ekvivalenten temu
func(func(func(func(s[0], s[1])), s[2]), s[3]), s[4]) - če
je s seznam s petimi elementi. Ali, v kodi (ki sicer ne zna
vsega, kar zna reduce):
def reduce(func, s):
acc = s[0]
for x in s[1:]:
acc = func(acc, x)
return accPo domače: reduce pokliče funkcijo na prvih dveh
elementih, nato na rezultatu tega klica in tretjem elementu, nato na
rezultatu tega klica in četrtem elementu... Spremenljivko
acc pa smo poimenovali po njeni vlogi: akumulator.
Če vemo, kaj so iteratorji in kaj počne next, znamo bolj
natančno (če ne, pa nič narobe, tudi gornje je dovolj dobro za
razumevanje, ki ga potrebujemo za uporabo funkcije):
def reduce(func, s, acc=None):
t = iter(s)
if acc is None:
acc = next(t)
for x in t:
acc = func(acc, x)
return accZ reduce se da početi zanimive stvari. Pripravimo si
nekaj funkcij (ki bi lahko bile tudi lambde, ampak recimo, da jih ne
znamo pisati).
def sestej(a, b):
return a + b
def zmnozi(a, b):
return a * b
def vrni_vecjega(a, b):
if a > b:
return a
else:
return b
def oba_resnicna(a, b):
return a and bPripravimo si še priložnostni seznam števil.
s = [4, 2, 6, 3]Z reduce lahko zdaj izračunamo vsoto elementov
seznama
reduce(sestej, s)15
produkt
reduce(zmnozi, s)144
in poiščemo največji element
reduce(vrni_vecjega, s)6
mimogrede pa še 10!, se pravi produkt števil do 10
reduce(zmnozi, range(1, 11))3628800
Če imamo seznam True-jev in False-ov, lahko
z reduce izračunamo njegovo konjunkcijo (and
prek vseh elementov`).
reduce(oba_resnicna, [True, True, True, True, True])True
reduce(oba_resnicna, [True, True, True, False, True])False
Imenitna reč, problem je le, da se nam teh funkcij ne da definirati
vnaprej, Pythonove lambde, s katerimi lahko funkcijo definiramo kar
sproti, znotraj klica reduce, pa so zelo kilave in tudi
nikoli ne bodo drugačne kot kilave.
Funkcije map, filter in reduce
- pri čemer se slednja v drugih jezikih bolj pogosto kot ne imenuje
fold - so osnovni elementi funkcijskega
programiranja.