Funksjonell og fasjonabel

Funksjonell og fungerende

1
2
3
4
5
from itertools import takewhile, count

is_small = lambda x: x < 100
square = lambda x: x**2
small_squared_integers = list(takewhile(is_small, map(square, count(0))))

reduce

Fold (også kalt reduce) er en operator som kombinerer en funksjon med en liste elementer. Dette kan brukes til å lage en verdi som "oppsummerer" lista. F.eks. vanlig summasjon i matematikk er en fold:

(1) i = 1 10 i = ( 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 )

er (nesten) det samme som foldl (+) 0 [1..10] i språk som Haskell, reduce(+, 1:10) i Julia eller reduce(add, range(1, 10+1), 0) i Python. Nullen i de to uttrykkene betegner start-leddet, dvs. en tom sum er alltid null. Det er likevel en liten forskjell: dette er egentlig en left fold, altså vi antar venstre-assosiativitet, så vi regner egentlig ut

(2) ( ( ( ( ( ( ( ( ( ( ( 0 ) + 1 ) + 2 ) + 3 ) + 4 ) + 5 ) + 6 ) + 7 ) + 8 ) + 9 ) + 10 )

Men siden addisjon er assosiativ, får vi samme svar. Dette er tanken bak funksjonaliteten til følgende kode

1
2
3
4
from functools import reduce

add = lambda current_sum, next_addend: current_sum + next_addend
number_sum = reduce(add, range(1, 100+1)) #equivalent to sum(range(1, 100+1))

add-funksjonen kan også importeres fra operator-biblioteket som følger med Python. Her kan det være greit å poengtere at dette kan gjøres med vanlige for-løkker:

1
2
3
initial_value = 0
for e in range(1, 10+1):
initial_value += e

men det ville jo ikke vært like gøy ...

1
2
multiply = lambda current_product, next_factor: current_product*next_factor
number_product = reduce(multiply, range(1, 10+1))

Her kan vi også importere mul fra operator istedenfor å lage denne selv. Python har to andre innebygde funksjoner som er basert på fold-mønsteret. Dette er all og any, som har tilsvarende implementasjon.

1
2
and_ = lambda current_bool, next_bool: current_bool and next_bool
number_all = reduce(and_, range(1, 10+1)) #equivalent to all(range(1, 10+1))
1
2
or_ = lambda current_bool, next_bool: current_bool or next_bool
number_all = reduce(or_, range(1, 10+1)) #equivalent to any(range(1, 10+1))

Med matematisk terminologi har vi en binær funksjon f ( a , b ) og en (endelig) ordnet liste ( x i ) (samt en mulig startverdi s 0 ). En venstrefold blir da

(3) f ( f ( f ( f ( s 0 , x 1 ) , x 2 ) ) , x n - 1 ) , x n )

En høyrefold blir tilsvarende

(4) f ( f ( f ( f ( s 0 , x n ) , x n - 1 ) ) , x 2 ) , x 1 )

filter

filter er en funksjon som filtrerer ut elementer ut ifra ett sannhets-kriterie. Den tar inn en unær boolsk funksjon og en liste.

1
2
3
4
5
is_odd = lambda x: x%2

for i in filter(is_odd, range(10)):
print(i, end=' ')
#1 3 5 7 9

Igjen, dette kan også skrives med vanlige for-løkker og if-setninger

1
2
3
4
5
6
is_odd = lambda x: x%2

for i in range(10):
if is_odd(i):
print(i, end=' ')
#1 3 5 7 9

Men fordi vi bare bruker oddetall i løkka, kan det være greit å filtrere bort partallene fra lista, istedenfor å sjekke dette i løkka.

Det var alt for denne gang. Nå blir det nok 4 måneder til neste blogginnlegg.

28.09.18