Functional programming

Basics

  • lambda - Create an anonymous function

  • filter(func, list) - Call func on every list item, return a new list of elements where func returned True

  • map(func, list) - Call func on every list item, return new list a returned results

  • reduce(func, list) - Call func with first to items of list, than call the result with the third, that result with the forth and so on

reduce(lambda x,y: x+y, [1,2,3])
-> (1 + 2) + 3
  • zip(list1, list2, listN) - creates list of tuples containing the n-th item of the input lists

  • A complete example

even = lambda x: x % 2
square = lambda x: x * x
map(square, filter(even, range(1,100)))
[1, 9, 25, 49, 81, 121, 169, 225, 289, 361, 441, 529, 625, 729, 841, 961, 1089, 1225, 1369, 1521, 1681, 1849, 2025, 2209, 2401, 2601, 2809, 3025, 3249, 3481, 3721, 3969, 4225, 4489, 4761, 5041, 5329, 5625, 5929, 6241, 6561, 6889, 7225, 7569, 7921, 8281, 8649, 9025, 9409, 9801]
  • The same with list comprehensions

[x*x for x in range(1,100) if x % 2]
  • find items contained in both lists

[x for x in list1 if x in list2]

Generators

  • Generator remember it’s state and return (yield) the next item

def gen_even_numbers():
     i = 2

     while True:
         yield i
         i += 2

gen = gen_even_numbers()
gen.next()
  • Generator expressions are like list comprehensions with round parentheses

gen = (x for x in filter(lambda x: x % 2 == 0, range(100)))

Coroutine

  • A coroutine is like a generator but excepts chunk-wise input

  • It executes till the next yield and wait for you to send data in

  • Dont forget the pretending next otherwise the coroutine will never reach the first yield

def receiver():
  while True:
    print "Waiting for data..."
    data = (yield)
    print "Got " + str(data)

recv = receiver().next()
recv.send("abc")
recv.close()

Functools

  • create new function with fixed parameter

def sum(a,b):
    return a + b

import functools
add_two = functools.partial(sum, b=2)

Closure

  • A closure is a function pointer with saved parameters

from urllib import urlopen

def page(url):
  def get():
    return urlopen(url).read()
  return get

codekid = page("http://www.codekid.net")
codekid()

Decorators

  • A Decorator is a function that wraps another function

  • code by hand

def hello(func):
  def callf(*args, **kwargs):
    print "Hello"
    func(*args, **kwargs)
    print "Bye"
  return callf
  • with functools

from functools import wraps
def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwds):
        print 'Calling decorated function'
        return func(*args, **kwds)
    return wrapper

Conditional decorator with arguments

from nose.tools import timed
import time

def not_on_travis(decorator):
  def wrapper(func):
    if os.getenv("TRAVIS"):
      return func
    else:
      return decorator(func)
  return wrapper

@not_on_travis(timed(0.1))
def say(what):
  print "You said " + what
  time.sleep(1)

Memoize Decorator

  • Caches function results for inputs

def memoize(f):
    cache = {}

    @wraps(f)
    def helper(x):
        if x not in cache:
            cache[x] = f(x)
        return cache[x]
    return helper