{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Functional Programming\n",
"One of the most popular programming paradigms is FP. \n",
"In this lecture, we show how we can use Python to carry out some basic FP.\n",
"\n",
"**Created and updated by** John C. S. Lui on October 12, 2020.\n",
"\n",
"**Important note:** *If you want to use and modify this notebook file, please acknowledge the author.*"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Functional programming\n",
"\n",
"- Treat input as \"set\"\n",
"- Define operations we want to do for each element of the set\n",
"\n",
"In functional programming, there are two important concepts:\n",
" - iterator\n",
" - passing function as parameter to another function\n",
"\n",
"So let us go over **iterator** first."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"## Iterators: list\n",
"for x in [1,2,3,4,5]:\n",
" print(\"Element is: \", x)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"## Iteators: character string\n",
"for c in \"John is a jerk!!!\":\n",
" print (\"Char. is: \", c)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Iterators: tuple\n",
"for x in (1,2,3,4,5):\n",
" print ('x is:', x)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Iterators: dictionary\n",
"\n",
"# When we use with dictionary, it loops over the keys of the dictionary\n",
"for key in {\"k1\": 'value1', \"k2\": 'value2', \"k3\": 15}:\n",
" print (\"key is: \", key)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Iterators\n",
"\n",
"- list, character strings, tuple and dictionary are all **iterable objects**.\n",
"\n",
"- A function *func* takes an iterable object and returns an iterator."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"iter_obj = iter([1,2,3,4,5]) #iter_obj is an iterable object\n",
"print(iter_obj)\n",
"print([1,2,3,4,5])\n",
"\n",
"# iter_obj.next() # display next item\n",
"print(next(iter_obj), next(iter_obj), next(iter_obj))\n",
"print(next(iter_obj),next(iter_obj))\n",
"\n",
"# can't access the next item\n",
"# next(iter_obj)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Python Function\n",
"- Name of a function is just a *reference* to an object representing that function\n",
"- We can assign that function to another variable\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import math\n",
"\n",
"mySqrt = math.sqrt # assign my math.sqrt function to the variable mySqrt\n",
"mySqrt(4.0)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- If a variable can be a reference to a function\n",
"- And we can pass variable as a parameter to a function\n",
"- This means we can also pass a function (or variable) as a parameter to another function !!!\n",
"\n",
"For detail, please the document:\n",
"http://python-history.blogspot.com/2009/02/first-class-everything.html\n",
"\n",
"Why is this **important**? Because in functional programming, we need the concept of:\n",
"- iterator\n",
"- passing function as parameter"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Transformation\n",
"\n",
"Often, our variables (or data) are represented using list, tuple or dictionary.
\n",
"We may want to carry out some *transformation* for all or a subet of elements in the variable.
\n",
"This can be achieved via \n",
"- mapping\n",
"- filtering\n",
"- reduction (for Python 2.x)\n",
"\n",
"Let's illustrate the *map()* function first.
\n",
"\n",
"The general syntax is:
\n",
" % *map(func, seq)*
\n",
"where *func* is a pre-defined function which will operate on **each element** of the *seq* sequence (e.g., list).\n",
"\n",
"Note that in Python 2.x, map returns a list, where each element of the result list is the result
\n",
"of the function *func* applied on the corresponding element of the list or tuple *seq*.
\n",
"With Python 3, *map()* returns an * **iterator** *."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# we have a list of integer and we want to multiply each elements in the list by 2 and add 1 to it.\n",
"\n",
"# First, define a function\n",
"def map_func1(x): \n",
" return x*2 + 1\n",
"\n",
"my_variable = [1,2,3,4,5] # define a list\n",
"\n",
"#apply map_func1 to my_variable, and convert the result to a list\n",
"print(\"my_variable is : \", my_variable)\n",
"\n",
"\n",
"my_variable = list (map(map_func1, range(0,11)))\n",
"print(\"my_variable is now : \", my_variable)\n",
"\n",
"my_variable = [ x+1 for x in map(map_func1, range(0,11)) ] # using list compreshension\n",
"print(\"my_variable is really :\", my_variable)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You can put **more than one input (or sequence)** as arguments."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# define function\n",
"def my_add (x,y): \n",
" return x+y\n",
"\n",
"my_variable = list(map(my_add, range(0,10), range(0,10)))\n",
"print(my_variable)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's see how *filter()* function works.\n",
"\n",
"The general syntax is:
\n",
" % *filter (func, seq)* \n",
" \n",
"where each element in the *seq* will be passed to the *func* to check the **boolean condition**. If it is satisfied, the element will be returned.
\n",
"\n",
"Hence, all elements will be returned as an **iterator** ."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# define a filter function\n",
"def my_func1(x): return x % 3 == 0 or x % 5 == 0 # return True or False\n",
"\n",
"def my_func2(x): return (x+1)%2 == 0 # return True or False\n",
"\n",
"my_variable = list(filter(my_func1, range(1,25)))\n",
"print(\"1. my_variable = \", my_variable)\n",
"\n",
"my_variable = [ x for x in filter(my_func2, range(1,25))] # use list comprehension !!!!\n",
"print(\"2. my_variable = \", my_variable)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's see how *reduce()* function works in Python 2.x.\n",
"\n",
"The general syntax is:
\n",
" % *reduce (func, seq)* \n",
" \n",
"returns a **single value** constructed by calling the *func* function on the
\n",
"first two items of the *seq*, then the result and the next item of *seq*
\n",
"will again be applied to the *func* function, and so on. Let's see an example (in Python 2.x):\n",
"\n",
" >>> def add(x,y): return x+y
\n",
" >>> reduce (add, range(1,11)) # this will return 55 in Python 2.x\n",
"\n",
"For Python 3.x, we have to first *import* the functools package of reduce:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from functools import reduce\n",
"\n",
"#define a reduce function\n",
"def my_add(x,y): return x+y\n",
"\n",
"reduce(my_add, range(1,11))\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Lambda function\n",
"\n",
"The lambda operator or lambda function is a way to create small *anonymous* functions, i.e. \n",
"functions without a name.
\n",
"\n",
"The general syntax of a *lambda* function is quite simple: \n",
"\n",
" >>> *lambda argument_list: expression*\n",
"\n",
"The argument list consists of a comma separated list of arguments, and the expression is
\n",
"an arithmetic expression using these arguments. One can assign the function to a variable to give it a name.\n",
"\n",
"Let's illustrate."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# define a function\n",
"def sum(x,y): return x + y\n",
"\n",
"# an alternate way to write this small function is via lambda\n",
"sum1 = lambda x, y : x + y\n",
"\n",
"print (sum (3,4))\n",
"print (sum1(3,4))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Combining lambda and map/filter function\n",
"\n",
"Let's see how can use lambda with other map/filter functions"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# use of lambda function\n",
"my_variable1 = list(map(lambda x,y: x+y, range(0,10), range(0,10)))\n",
"print (\"my_variable1 = \", my_variable1)\n",
"\n",
"# use lambda and list comprehension\n",
"my_variable2 = [x for x in map(lambda x,y: x+y, range(0,10), range(0,10))] \n",
"print(\"my_variable2 = \", my_variable2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## List comprehension\n",
"\n",
"General syntax:\n",
"\n",
" [ *expression* **for** *var* **in** *iterator or list* **if** expression ]\n",
"\n",
"- **if** part is optional\n",
"- Each element in the list (or iterator) is examined by the **if** statement\n",
"- If elemnet passes **if** check, then *expression* is evaluated and result will be added to the list (or iterator)\n",
"\n",
"Let's illustrate."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# define a list\n",
"a_list = [1,2,3,4,5]\n",
"print(\"The result is: \", [x*2 for x in a_list if x<4])"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# define a dictionary\n",
"my_dictionary = {1:'john', 2:'jack', 3:'alice', 4:'helen'}\n",
"\n",
"a_list = [my_dictionary[i].title() for i in my_dictionary.keys()]\n",
"print('a_list = ', a_list)\n",
"\n",
"a_list = [my_dictionary[i].title() for i in my_dictionary.keys() if i %2 == 0]\n",
"print('a_list = ', a_list)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}