CS 2120: Class #3
=================
.. image:: ../img/Eniac.jpeg
Functions
^^^^^^^^^
* Probably the most important things in a programming language.
* If you're going to pay attention only once this term... now's the time.
* Script/program files are a nice way to organize many statements
* You eventually find yourself writing the same series of statements over and over
* (or cutting and pasting in your editor)
* There's gotta' be a better way!
.. image:: ../img/nomilk.gif
* We want a way to *group together sequence of statements that we frequently reuse*
* In Python, we do this with a *function*. Here's one now::
def my_function(a_parameter):
b = a_parameter * 2
print b
* Once you've defined a function, you can *call* it from the Python interpreter in
exactly the same way you'd call a *built-in function* like ``print``.
* So let's use our function:
>>> my_function(2)
4
>>> my_function(7)
14
>>> my_function("Friends")
FriendsFriends
.. commented out for now .. image:: ../img/almostmilk.gif
* When we call ``my_function``, the Python interpreter executes the statements that
make up the function, in order.
* Functions make code easy to reuse, and easy to read. More importantly they *facilitate abstraction*.
.. image:: ../img/nowmilk.gif
.. admonition:: Quick Activity
Write your own function to do something with math.
Function Parameters
^^^^^^^^^^^^^^^^^^^^
* Note carefully the parameter (``a_parameter``) in the definition of ``my_function``. When you are defining a function, you
want the function to be very *general*. You want it to work with *any possible* parameter that
someone might want to give it.
* Imagine an ``add_print(a,b)`` function that adds two numbers and prints the result. You want it to add *any* two numbers, not
just two specific numbers. A function ``add_print(3,5)`` that can only add 3 to 5 wouldn't be very
useful. It would only ever print ``8``. So we introduce *parameters*.
* Parameters are like variables. When you *call* the function, the first thing that happens is the
parameter values get set. Let's go ahead and build our ``add_print`` function::
def add_print(a,b):
print a+b
* Now that the function is defined, we can *call* it. Like this:
>>> add_print(5,2)
7
* The *call* ``add_print(5,2)`` gets handled like this:
* The interpreter checks to see if it knows about a function named ``add_print``
* We just defined ``add_print``, so it does.
* When we defined it, we told the interpreter it should have two parameters: ``a`` and ``b``.
* The interpreter now takes the values in the call (in this case, ``5`` and ``2``) and assigns those
values to the function parameters ``a`` and ``b``.
* In other words, the first thing the interpreter does in this case is set ``a = 5`` and ``b = 2``
* Then the interpreter executes the body of the function, with the parameters having their new values.
* What happens if we don't give it enough, or too many parameters?
.. raw:: html
Abstraction: first steps
^^^^^^^^^^^^^^^^^^^^^^^^
* Why is abstraction important?
.. admonition:: Activity
Write down a "program" to make spaghetti (not in python, like on paper). You can only use the following statements:
* ``locate [object]``
* ``grasp [limb]``
* ``release [limb]``
* ``move_limb_to [location]``
* ``wait [time in seconds]``
Assume you start from a clean, empty, kitchen.
.. admonition:: Activity
Write down a "program" to make spaghetti (not in python, like on paper). You can use plain English prose and assume you are addressing a human being.
* You've now written programs at two levels of abstraction. Which was easier?
* Functions allow us to build *towers of abstraction*.
* A low level function might worry about how to set the individual pixels of the display to show the letter ``A`` .
* Would you want to cut-and-paste that code every time you needed to print ``A``?
* Instead, we have a function called ``print`` that hides all those messy details from us.
* We call ``print``, ``print`` calls other functions, which call other functions, which call other functions...
* Without organizing things into *levels of abstraction* writing complex software would be impossibly difficult.
* Forget programming. In the rest of your scientific life, learning to think in terms of levels of abstraction is
a hugely important skill.
* e.g., if you're a Neuroscientist doing an fMRI experiment... should you be worrying about the state of a
particular type of serotonergic receptor in a single neuron in the cortex?
Back to concrete things...
^^^^^^^^^^^^^^^^^^^^^^^^^^
* The general format for defining a function is::
def function_name(p1,p2,p3,p4, ... ):
statement 1
statement 2
...
statement m
* ``function_name`` is... the name of the function
* ``p1, p2`` , etc. are called the *parameters*, you can have as many as you like
* You tell Python which statements make up the *body* of the function by using *indentation*.
* This is a somewhat unique feature of Python. Many other languages use pairs like ``begin, end`` , ``do, done`` or ``{, }``
to delimit the body of a function.
* Using whitespace might take some getting used to, but there is a huge benefit: your
code "runs the way it looks". If you've ever wasted time counting ``}`` s... you know what I mean.
* However, white spaces can be uncomfortable too... sorry... deal with it...
.. admonition:: Activity
Write a function ``catstr`` which takes two strings as parameters and then prints out the concatenation of the
strings. e.g., if I call ``catstr('Hello ','world!')`` it will print ``Hello world!``.
.. raw:: html
.. admonition:: Activity
Now write a function ``crosscat`` that will take *four* strings and print out the concatenation of the first and third string,
and then, on a new line, the concatenation of the second and fourth string.
**BUT**: your function isn't allowed to use a ``print`` statement! You can, however, use your ``catstr`` function.
.. raw:: html
Execution Flow
^^^^^^^^^^^^^^^
* The Python interpreter executes one statement at a time
* To make sense of programs, we need to know *which* instruction gets executed *when*.
* In a program, the statements get executed in the order in which they appear in the program, top to bottom of the file.
* Later, we'll learn how to jump around.
* What happens when a function gets called? Let's trace through this program::
def dostuff(a,b):
c = b*2
d = (a+4)*2
c = d + c
return c
x = 2
y = 3
print dostuff(x,y)
print "where am I?"
* So what happens is:
* The interperter makes a note of where the function is being called from.
* The *flow of execution* passes to the function
* The interpreter executes each statement in the function, in order.
* at the end of the function, control returns to the point from which the function was called.
Function values
^^^^^^^^^^^^^^^^
* Notice how ``dostuff`` ended with a ``return`` statement.
* The ``return`` statement tells Python: "*return* this value to whoever called this function"
* With ``return``, *functions* evaluate into *values*.
* Consider:
>>> print dostuff(2,2)
16
>>> print dostuff(4,4)
24
>>> print dostuff(2,2) + dostuff(4,4)
40
* When the interpreter hits a ``dostuff``, it goes and *does stuff* (executes the function).
* Because that function ends in a ``return``, when execution flow comes back to the calling
program, the call to ``dostuff`` gets replaced with whatever value got ``return`` ed.
.. admonition:: Quick Activity
* Write a function ``nostuff(a,b)`` which is identical to ``dostuff(a,b)`` **except** it does not contain a ``return`` statement.
* What happens when you try this?
>>> print nostuff(2,2)
* What happens when you try this?
>>> print dostuff(2,2)
.. admonition:: Activity
Write a function ``compmag(r,m)`` to compute, and return, the magnitude of a complex number.
It should take the real component of the number as parameter ``r`` and the imaginary component as ``m``.
.. Remember that :math:`|r + mi| = \sqrt{r^2 + m^2}`. Say, does Python have a square root function?
.. How would you find it?
Remember that | r + mi | = sqrt(r^2 + m^2). Say, does Python have a square root function?
How would you find it?
.. raw:: html
Composition
^^^^^^^^^^^^
* Python functions can be *composed* just like mathematical functions.
* We've already seen ``print`` composed with ``dostuff``
* We can nest functions, too:
>>> dostuff(dostuff(2,2),dostuff(2,2))
72
* If you get confused tracing nested functions, just remember:
* Functions get *evaluted* and turned into values
* Find a function you can evaluate
* Evaluate it
* Cross out the function and replace it with the *value* it returns
* Keep doing this until you're down to one value.
.. admonition:: Activity
Figure out the value of ``dostuff(dostuff(2,2), (dostuff(2,2) + dostuff(4,4)) )`` using only *pen and paper*. No computers!
.. admonition:: Activity
Figure out the value of ``nostuff(nostuff(2,2), (nostuff(2,2) + nostuff(4,4)) )`` using only *pen and paper*. No computers!
Variable scope (not the mouthwash)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* If you set a variable inside a function, it is *local* to that function.
* No other function can see a function's local variables. They are *local*. Consider this code::
def domore(a,b):
c = 2*a + b
return c
* What happens if I do this:
>>> print domore(4,4)
12
>>> print c
NameError: name 'c' is not defined
* Error! But ``c`` is defined in ``domore``! Why did we get an error?
* Moral of the story: variables have *scope*. This can actually be a suprisingly delicate concept
and we'll come back to it later.
Optional parameters for functions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Sometimes you want a function to have an optional parameter, with a pre-specified default value.
* This is done very easily::
def my_function(a,b,c=3):
do_stuff()
* When you call ``my_function(5,12)``, ``a`` will have value ``5``, ``b`` value ``12`` and ``c`` value ``3``.
* Because we specified a *default* value for ``c``, we don't have to provide one when we call the function.
* If we want to *override* the default though, we can: ``my_function(4,3,2)``.
* A reasonable example::
def time_to_fall(d, a = 9.807):
return math.sqrt(2*d/a)
Import
^^^^^^^
* Another practical matter: sometimes you want to make a big library of functions. Maybe related
to analysis data from your research.
* You'd like to access some of those functions from another program that you're writing.
* If you put your functions in a file called 'myfuncs.py', you can *import* them into another program like this:
>>> from myfuncs import *
* (The ``*`` here means *everything*)
* You could also use:
>>> import myfuncs
* **BUT**, this adds a namespace. To access a function called ``dostuff`` in the file ``myfunc`` after this
style of ``import``, you'd have to type::
>>> myfuncs.dostuff(...)
Import --- MORE
^^^^^^^^^^^^^^^
* Can also import other people's functions
* >>> import math
* >>> import numpy
For next class
^^^^^^^^^^^^^^^
* Read `chapter 4 of the text `_