{ "cells": [ { "cell_type": "markdown", "metadata": { "deletable": true, "editable": true }, "source": [ "## Introduction to Scientific Computing\n", "### Lecture 08: Recursion and Object Oriented Programming with Classes\n", "#### J.R. Gladden, Spring 2018, Univ. of Mississippi" ] }, { "cell_type": "markdown", "metadata": { "deletable": true, "editable": true }, "source": [ "**Recursion** is an interesting technique in which a function calls *itself*. The key bit is the function must be called with an argument that changes with each iteration **and** there must be a test of that argument to break out of the cycle. A classical example is a factorial function which is defined as:\n", "$$ N! = N(N-1)(N-2)(N-3)...1 $$ where $$ 1! = 0! = 1 $$\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false, "deletable": true, "editable": true }, "outputs": [], "source": [ "def factorial(n):\n", "\tnbang = n\n", "\tif n==0: return 1\n", "\t#print n\n", "\tnbang*=factorial(n-1)\n", "\treturn nbang" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false, "deletable": true, "editable": true }, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "factorial(1)" ] }, { "cell_type": "markdown", "metadata": { "deletable": true, "editable": true }, "source": [ "Recursion can be used in a classic search algorithm called a **binary search** in which a sorted set is repeatedly cut in half to quickly bracket a particular value. " ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false, "deletable": true, "editable": true }, "outputs": [], "source": [ "def BinSearch(S,N,Low=0,High='dummy',counter=0):\n", "\t# Low and High are positions in the list (indices)\n", "\tif High=='dummy': High = len(S)\n", "\tprint \"Range for iteration %00i: %0000i \" % (counter,High-Low)\n", "\tif Low >= High:\n", "\t\tprint 'The number %i is not in the list.'%N\n", "\t\treturn -1\n", "\tMid = int((High + Low)/2.0)\n", "\tif N == S[Mid]:\n", "\t\tprint 'The number %i was found at position %i in the list in %i iterations ' % (N,Mid,counter)\n", "\t\treturn Mid\n", "\telif N < S[Mid]: \n", "\t\tBinSearch(S,N,Low=Low,High=Mid-1,counter=counter+1)\t\n", "\telse: \n", "\t\tBinSearch(S,N,Low=Mid+1,High=High,counter=counter+1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let's test it with a large set of random numbers..." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false, "deletable": true, "editable": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Range for iteration 0: 100000 \n", "Range for iteration 1: 49999 \n", "Range for iteration 2: 24998 \n", "Range for iteration 3: 12498 \n", "Range for iteration 4: 6248 \n", "Range for iteration 5: 3123 \n", "Range for iteration 6: 1560 \n", "Range for iteration 7: 779 \n", "Range for iteration 8: 388 \n", "Range for iteration 9: 193 \n", "Range for iteration 10: 96 \n", "Range for iteration 11: 47 \n", "Range for iteration 12: 22 \n", "Range for iteration 13: 10 \n", "The number 350 was found at position 358 in the list in 13 iterations \n" ] } ], "source": [ "import random\n", "N=int(1e5)\n", "S=[random.randint(0,N) for i in range(N) ]\n", "S.sort()\n", "target = 350\n", "position = BinSearch(S,target,0,len(S))" ] }, { "cell_type": "markdown", "metadata": { "deletable": true, "editable": true }, "source": [ "---\n", "** Object Oriented** programming has a different structure than what we have been doing. Up to this point, we have mostly been doing *procedural* programming. OO programming is powerful and flexible - one can creat new data types (or objects) and build in tools (or methods) for these objects and well as data (or attributes) for objects. And each instance of an object can carry different attributes with it." ] }, { "cell_type": "markdown", "metadata": { "collapsed": false, "deletable": true, "editable": true }, "source": [ "### Terminology:\n", "- **Class**: a definitition of an object type. It defines methods and attributes for each object\n", "- **Methods**: these \"look\" and act like functions within the class that perform some task associated with an object\n", "- **Attributes**: data or parameters which are associated with an object\n", "- **Instance**: When an object is created by calling the Class you have defined. You can have multiple instances of any given object." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here is an example of an Electric charge object. Attributes of any charged object might be the charge and mass. You might want the ability to switch the polarity on a charge as well as methods to get or set charge and mass values - these are often call \"getter\" and \"setter\" methods." ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false, "deletable": true, "editable": true }, "outputs": [], "source": [ "class Charge():\n", " def __init__(self):\n", " #Provide some default properties which can be changed later\n", " #Everything in here will be executed when a \"Charge\" instance\n", " #is created.\n", " self.charge = 1.0\n", " self.mass = 1.0\n", " \n", " def setCharge(self,value):\n", " self.charge = value\n", " \n", " def getCharge(self):\n", " return self.charge\n", " \n", " def getPolarity(self):\n", " \n", " if self.charge > 0.0: return '+'\n", " elif self.charge == 0.0: return '0'\n", " else: return '-'\n", " \n", " def switchPolarity(self):\n", " self.charge = - self.charge\n", " \n", " def setMass(self,value):\n", " self.mass = value\n", " \n", " def getMass(self):\n", " return self.mass \n" ] }, { "cell_type": "markdown", "metadata": { "deletable": true, "editable": true }, "source": [ "Now that the Class is defined, we can create multiple *instances* of the charge object. We'll call them $Q_1$ and $Q_2$. NOTE: we could create hundreds (or thousands) of independent charge objects and have them interact in a simulation. Each would carry is own set of parameters." ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": true, "deletable": true, "editable": true }, "outputs": [], "source": [ "Q1 = Charge()\n", "Q2 = Charge()\n" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false, "deletable": true, "editable": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3.5\n", "1.0\n" ] } ], "source": [ "Q1.setCharge(3.5)\n", "print(Q1.getCharge())\n", "print(Q2.getCharge())" ] }, { "cell_type": "markdown", "metadata": { "deletable": true, "editable": true }, "source": [ "Here's a little more complicated class which describes a \"Star\" object with methods to compute the volume and density of the star. Note we've also included a docstring which describes the methods and attributes defined. This will pop up if someone calls help on this object. Also note how we can use one method in another method (volume is used in the density method here).\n" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "collapsed": false, "deletable": true, "editable": true }, "outputs": [], "source": [ "class Star():\n", " '''\n", " A Star Class.\n", " Methods:\n", " .getMass() and .setMass(m) - gets or sets the mass of the star (in Kg)\n", " .getRadius() and .setRadrius(R) - gets and sets radius of star (in m)\n", " .calcVolume() - returns the volume of the star (in m^3)\n", " .calcDensity() - returns the average density (in Kg/m^3)\n", " Attributes:\n", " self.R - radius (in m)\n", " self.M - mass (in Kg)\n", " History: last updated 2/12/2018 by JRG\n", " '''\n", " def __init__(self):\n", " self.R = 10.**6 #radius in meter\n", " self.M = 10.**9 #mass in kg\n", " \n", " def getMass(self):\n", " return self.M\n", " \n", " def getRadius(self):\n", " return self.R\n", " \n", " def setRadius(self,R):\n", " self.R = R\n", " \n", " def setMass(self,M):\n", " self.M = M\n", " \n", " def calcVolume(self):\n", " '''\n", " Returns the volume in m^3\n", " '''\n", " self.volume = 4./3. * 3.14159 * self.R**3\n", " return self.volume\n", " \n", " def calcDensity(self):\n", " '''\n", " Returns the density in Kg/m^3\n", " '''\n", " self.density = self.M / self.calcVolume()\n", " # note how you can call another method within a method - cool!\n", " return self.density" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "collapsed": false, "deletable": true, "editable": true }, "outputs": [], "source": [ "star1 = Star()\n", "star2 = Star()" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "collapsed": false, "deletable": true, "editable": true }, "outputs": [ { "data": { "text/plain": [ "4.188786666666666e+18" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "star2.calcVolume()" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "collapsed": false, "deletable": true, "editable": true }, "outputs": [], "source": [ "star1.setRadius(5.5e3)" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "collapsed": false, "deletable": true, "editable": true }, "outputs": [ { "data": { "text/plain": [ "696909381666.6666" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "star1.calcVolume()" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "0.001434906784592982" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "star1.calcDensity()" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "2.3873261628665745e-10" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "star2.calcDensity()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 2", "language": "python", "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.13" } }, "nbformat": 4, "nbformat_minor": 0 }