diff options
-rw-r--r-- | advanced_python/exercises.rst | 7 | ||||
-rw-r--r-- | advanced_python/lambda.rst | 225 | ||||
-rw-r--r-- | advanced_python/slides/lambda.tex | 206 |
3 files changed, 438 insertions, 0 deletions
diff --git a/advanced_python/exercises.rst b/advanced_python/exercises.rst index ccec1ef..eeaa323 100644 --- a/advanced_python/exercises.rst +++ b/advanced_python/exercises.rst @@ -39,6 +39,13 @@ Exercises #. Create a sequence of images in which the damped oscillator (:math:`$e^{-x/10}sin(x)$`) slowly evolves over time. +#. Given a list of numbers, find all the indices at which 1 is present. + numbers = [1, 1, 3, 4, 3, 6, 7, 8, 1, 2, 4, 1] + +#. Given a list of numbers, find all the indices at which 1 is present. + numbers = [1, 1, 3, 4, 3, 6, 7, 8, 1, 2, 4, 1]. Solve the problem using a + functional approach. + .. Local Variables: mode: rst diff --git a/advanced_python/lambda.rst b/advanced_python/lambda.rst new file mode 100644 index 0000000..4660300 --- /dev/null +++ b/advanced_python/lambda.rst @@ -0,0 +1,225 @@ +Functional programming +====================== + +In this section, we shall look at a different style of programming called +Functional programming, which is supported by Python. + +The fundamental idea behind functional programming is that the output of a +function, should not depend on the state of the program. It should only +depend on the inputs to the program and should return the same output, +whenever the function is called with the same arguments. Essentially, the +functions of our code, behave like mathematical functions, where the output +is dependent only on the variables on which the function depends. There is +no "state" associated with the program. + +Let's look at some of the functionality that Python provides for writing +code in the Functional approach. + +List Comprehensions +------------------- + +Let's take a very simple problem to illustrate how list comprehensions +work. Let's say, we are given a list of weights of people, and we need to +calculate the corresponding weights on the moon. Essentially, return a new +list, with each of the values divided by 6.0. + +It's a simple problem, and let's first solve it using a ``for`` loop. We +shall initialize a new empty, list and keep appending the new weights +calculated + +:: + + weights = [30, 45.5, 78, 81, 55.5, 62.5] + + weights_moon = [] + + for w in weights: + weights_moon.append(w/6.0) + + print weights_moon + +We had to initialize an empty list, and write a ``for`` loop that appends +each of the values to the new list. List comprehensions provide a way of +writing a one liner, that does the same thing, without resorting to +initializing a new empty list. Here's how we would do it, using list +comprehensions + +:: + + [ w/6.0 for w in weights ] + +As we can see, we have the same list, that we obtained previously, using +the ``for`` loop. + +Let's now look at a slightly more complex example, where we wish to +calculate the weights on the moon, only if the weight on the earth is +greater than 50. + +:: + + weights = [30, 45.5, 78, 81, 55.5, 62.5] + weights_moon = [] + for w in weights: + if w > 50: + weights_moon.append(w/6.0) + + print weights_moon + +The ``for`` loop above can be translated to a list comprehension as shown + +:: + + [ w/6.0 for w in weights if w>50 ] + +Now, let's change the problem again. Say, we wish to give the weight on the +moon (divide by 6), when the weight is greater than 50, otherwise triple +the weight. + +:: + + weights = [30, 45.5, 78, 81, 55.5, 62.5] + weights_migrate = [] + for w in weights: + if w > 50: + weights_migrate.append(w/6.0) + else: + weights_migrate.append(w * 3.0) + + print weights_migrate + +This problem, however, cannot be translated into a list comprehension. We +shall use the ``map`` built-in, to solve the problem in a functional +style. + +``map`` +------- + +But before getting to the more complex problem, let's solve the easier +problem of returning the weight on moon for all the weights. + +The ``map`` function essentially allows us to apply a function to all the +elements of a list, and returns a new list that consists of the outputs +from each of the call. So, to use ``map`` we need to define a function, +that takes an input and returns a sixth of it. + +:: + + def moonize(weight): + return weight/6.0 + +Now, that we have our ``moonize`` function, we can pass the function and the +list of weights as an argument to ``map`` and get the required list. + +:: + + map(moonize, weights) + +Let's define a new function, that compares the weight value with 50 and +returns a new value based on the condition. + +:: + + def migrate(weight): + if weight < 50: + return weight*3.0 + else: + return weight/6.0 + +Now, we can use ``map`` to obtain the new weight values + +:: + + map(migrate, weights) + +As you can see, we obtained the result, that we obtained before. + +But, defining such functions each time, is slightly inconvenient. We are +not going to use these functions at any later point, in our code. So, it is +slightly wasteful to define functions for this. Wouldn't it be nice to +write them, without actually defining functions? Enter ``lambda``! + +``lambda`` +---------- + +Essentially, lambda allows us to define small functions anonymously. The +first example that uses the ``moonize`` function can now be re-written as +below, using the lambda function. + +:: + + map(lambda x: x/6.0, weights) + +``lambda`` above is defining a function, which takes one argument ``x`` and +returns a sixth of that argument. The ``lambda`` actually returns a +function object which we could in fact assign to a name and use later. + +:: + + l_moonize = lambda x: x/6.0 + +The ``l_moonize`` function, now behaves similarly to the ``moonize`` +function. + +The ``migrate`` function can be written using a lambda function as below + +:: + + l_migrate = lambda x: x*3.0 if x < 50 else x/6.0 + + +If you observed, we have carefully avoided the discussion of the example +where only the weights that were above 50 were calculated and returned. +This is because, this cannot be done using ``map``. We may return ``None`` +instead of calculating a sixth of the element, but we cannot ensure that +the element is not present in the new list. + +This can be done using ``filter`` and ``map`` in conjunction. + +``filter`` +---------- + +The ``filter`` function, like the ``map`` takes two arguments, a function +and a sequence and calls the function for each element of the sequence. The +output of ``filter`` is a sequence consisting of elements for which the +function returned ``True`` + +The problem of returning a sixth of only those weights which are more than +50, can be solved as below + +:: + + map(lambda x: x/6.0, filter(lambda x: x > 50, weights)) + +The ``filter`` function returns a list containing only the values which are +greater than 50. + +:: + + filter(lambda x: x > 50, weights) + + +``reduce`` +---------- + +As, the name suggests, ``reduce`` reduces a sequence to a single object. +``reduce`` takes two arguments, a function and a sequence, but the function +should take two arguments as input. + +``reduce`` initially passes the first two elements as arguments, and +continues iterating of the sequence and passes the output of the previous +call with the current element, as the arguments to the function. The final +result therefore, is just a single element. + +:: + + reduce(lambda x,y: x*y, [1, 2, 3, 4]) + +multiplies all the elements of the list and returns ``24``. + +.. + Local Variables: + mode: rst + indent-tabs-mode: nil + sentence-end-double-space: nil + fill-column: 75 + End: diff --git a/advanced_python/slides/lambda.tex b/advanced_python/slides/lambda.tex new file mode 100644 index 0000000..2ac7701 --- /dev/null +++ b/advanced_python/slides/lambda.tex @@ -0,0 +1,206 @@ +\section{Functional programming} + +\begin{frame}[fragile] + \frametitle{List Comprehensions} + \begin{itemize} + \item A different style of Programming + \item Functions `emulate' mathematical functions + \item Output depends only on input arguments + \item There is no `state' associated with the program + \end{itemize} +\end{frame} + +\begin{frame}[fragile] + \frametitle{List Comprehensions} + \begin{block}{} + Given a list of weights of people, and we need to + calculate the corresponding weights on the moon. Return a new + list, with each of the values divided by 6.0. + \end{block} + + \begin{itemize} + \item Solution using \texttt{for} loop is shown + \end{itemize} + \begin{lstlisting} + weights = [30, 45.5, 78, 81, 55.5, 62.5] + weights_moon = [] + for w in weights: + weights_moon.append(w/6.0) + print weights_moon + \end{lstlisting} +\end{frame} + +\begin{frame}[fragile] + \frametitle{List Comprehensions \ldots} + \begin{itemize} + \item List comprehensions are compact and readable + \end{itemize} + \begin{lstlisting} + [ w/6.0 for w in weights ] + \end{lstlisting} +\end{frame} + +\begin{frame}[fragile] + \frametitle{List Comprehensions \ldots} + \begin{itemize} + \item Return the weight on moon, only if weight on earth > 50 + \end{itemize} + \begin{lstlisting} + weights = [30, 45.5, 78, 81, 55.5, 62.5] + weights_moon = [] + for w in weights: + if w > 50: + weights_moon.append(w/6.0) + + print weights_moon + \end{lstlisting} + \begin{itemize} + \item List comprehension that checks for a condition + \end{itemize} + \begin{lstlisting} + [ w/6.0 for w in weights if w>50 ] + \end{lstlisting} +\end{frame} + +\begin{frame}[fragile] + \frametitle{List Comprehensions \ldots} + \begin{itemize} + \item if weight > 50, return weight/6.0 + \item else, return weight*3.0 + \end{itemize} + \begin{lstlisting} + weights = [30, 45.5, 78, 81, 55.5, 62.5] + weights_migrate = [] + for w in weights: + if w > 50: + weights_migrate.append(w/6.0) + else: + weights_migrate.append(w * 3.0) + + print weights_migrate + \end{lstlisting} + \begin{itemize} + \item This problem \alert{CANNOT} be solved using list + comprehensions + \item Try \texttt{map} + \end{itemize} +\end{frame} + +\begin{frame}[fragile] + \frametitle{\texttt{map}} + \begin{itemize} + \item Takes a function and a sequence as arguments + \item Calls function with each element of sequence as argument + \item Returns a sequence with all the results as elements + \item We solve the easier problem first, using \texttt{map} + \end{itemize} + + \begin{lstlisting} + def moonize(weight): + return weight/6.0 + + map(moonize, weights) + \end{lstlisting} +\end{frame} + +\begin{frame}[fragile] + \frametitle{\texttt{map} \ldots} + \begin{itemize} + \item Takes a function and a sequence as arguments + \item Calls function with each element of sequence as argument + \item Returns a sequence with all the results as elements + \item We solve the easier problem first, using \texttt{map} + \end{itemize} + + \begin{lstlisting} + def moonize(weight): + return weight/6.0 + + map(moonize, weights) + \end{lstlisting} +\end{frame} + +\begin{frame}[fragile] + \frametitle{\texttt{map} \ldots} + \begin{lstlisting} + def migrate(weight): + if weight < 50: + return weight*3.0 + else: + return weight/6.0 + \end{lstlisting} + + \begin{itemize} + \item \texttt{migrate} compares weight with 50 and returns the + required value. + \item We can now use \texttt{map} + \end{itemize} + \begin{lstlisting} + map(migrate, weights) + \end{lstlisting} + \begin{itemize} + \item Now, we wish to get away with the function definition + \end{itemize} +\end{frame} + +\begin{frame}[fragile] + \frametitle{\texttt{lambda}} + \begin{itemize} + \item Allows function definition, anonymously + \end{itemize} + \begin{lstlisting} + map(lambda x: x/6.0, weights) + \end{lstlisting} + \begin{itemize} + \item \texttt{lambda} actually returns a function which we could in + fact assign to a name and use later. + \end{itemize} + \begin{lstlisting} + l_moonize = lambda x: x/6.0 + map(l_moonize, weights) + \end{lstlisting} + \begin{lstlisting} + l_migrate = lambda x: x*3.0 if x < 50 else x/6.0 + \end{lstlisting} +\end{frame} + +\begin{frame}[fragile] + \frametitle{\texttt{filter}} + + \begin{itemize} + \item We avoided discussion of problem of returning new weights only + when actual weight is more than 50. + \item \texttt{filter} can be used to filter out ``bad'' weights + \item Later, we could use \texttt{map} + \end{itemize} + \begin{lstlisting} + filter(lambda x: x > 50, weights) + \end{lstlisting} + \begin{itemize} + \item Takes a function and a sequence + \item Returns a sequence, containing only those elements of the + original sequence, for which the function returned \texttt{True} + \end{itemize} + \begin{lstlisting} + map(lambda x: x/6.0, filter(lambda x: x > 50, weights)) + \end{lstlisting} +\end{frame} + +\begin{frame}[fragile] + \frametitle{\texttt{reduce}} + \begin{itemize} + \item ``reduces'' a sequence + \end{itemize} + \begin{lstlisting} + reduce(lambda x,y: x*y, [1, 2, 3, 4]) + \end{lstlisting} + \begin{itemize} + \item Takes function and sequence as arguments; the function should + take two arguments + \item Passes first two elements of sequence, and continues to move + over the sequence, passing the output in the previous step and the + current element as the arguments + \item The function above essentially calculates $((1*2)*3)*4$ + \end{itemize} +\end{frame} + |