<jupyter_start><jupyter_text>Using Functions **CS1302 Introduction to Computer Programming**___<jupyter_code>%reload_ext mytutor<jupyter_output><empty_output><jupyter_text>Motivation **How to reuse code so we can write less?** When we write a loop, the code is executed multiple times, once for each iteration. This is a simple form of *code reuse* that - gives your code an elegant *structure* that- can be executed efficiently by a computer, and- *interpreted* easily by a programmer. **How to repeat execution at different times, in different programs, and in slightly different ways?**  Functions **How to calculate the logarithm?** There is no arithmetic operator for logarithm.  Do we have to implement it ourselves? We can use the function `log` from the [`math` *module*](https://docs.python.org/3/library/math.html):<jupyter_code>from math import log
log(256, 2)  # log base 2 of 256<jupyter_output><empty_output><jupyter_text>The above computes the base-$2$ logarithm, $\log_2(256)$. Like functions in mathematics, a computer function `log` - is *called/invoked* with some input *arguments* `(256, 2)` following the function, and- *returns* an output value computed from the input arguments.<jupyter_code># A function is callable while an integer is not
callable(log), callable(1)<jupyter_output><empty_output><jupyter_text>Unlike mathematical functions:- A computer function may require no arguments, but we still need to call it with `()`.<jupyter_code>input()<jupyter_output>abc<jupyter_text>- A computer function may have side effects and return `None`.<jupyter_code>x = print()
print(x, 'of type', type(x))<jupyter_output>None of type <class 'NoneType'><jupyter_text>An argument of a function call can be any expression.<jupyter_code>print('1st input:', input(), '2nd input', input())<jupyter_output>abc
def
1st input: abc 2nd input def<jupyter_text>Note also that- the argument can also be a function call like function composition in mathematics. - Before a function call is executed, its arguments are evaluated first from left to right. **Why not implement logarithm yourself?** - The function from standard library is efficiently implemented and thoroughly tested/documented.- Knowing what a function does is often insufficient for an efficient implementation.      (See [how to calculate logarithm](https://en.wikipedia.org/wiki/LogarithmCalculation) as an example.) Indeed, the `math` library does not implement `log` itself:> **CPython implementation detail:** The `math` module consists mostly of thin *wrappers* around the platform C math library functions. - [pydoc last paragraph](https://docs.python.org/3/library/math.html)(See the [source code wrapper for `log`](https://github.com/python/cpython/blob/457d4e97de0369bc786e363cb53c7ef3276fdfcd/Modules/mathmodule.cL731).)  **Exercise** What is a function in programming? - A function is a structure that allows a piece of code to be reused in a program.  - A function can adapt its computations to different situations using input arguments.    Import Functions from Modules **How to import functions?** We can use the [`import` statement](https://docs.python.org/3/reference/simple_stmts.htmlimport) to import multiple functions into the program *global frame*.<jupyter_code>%%mytutor -h 300
from math import log10, ceil
x = 1234
print('Number of digits of x:', ceil(log10(x)))<jupyter_output><empty_output><jupyter_text>The above import both the functions `log10` and `ceil` from `math` to compute the number $\lceil \log_{10}(x)\rceil$ of digits of a *strictly positive* integer $x$. **How to import all functions from a library?**<jupyter_code>%%mytutor -h 300
from math import *  # import all except names starting with an underscore
print('{:.2f}, {:.2f}, {:.2f}'.format(sin(pi / 6), cos(pi / 3), tan(pi / 4)))<jupyter_output><empty_output><jupyter_text>The above uses the wildcard `*` to import ([nearly](https://docs.python.org/3/tutorial/modules.htmlmore-on-modules)) all the functions/variables provided in `math`. **What if different packages define the same function?**<jupyter_code>%%mytutor -h 300
print('{}'.format(pow(-1, 2)))
print('{:.2f}'.format(pow(-1, 1 / 2)))
from math import *
print('{}'.format(pow(-1, 2)))
print('{:.2f}'.format(pow(-1, 1 / 2)))<jupyter_output><empty_output><jupyter_text>- The function `pow` imported from `math` overwrites the built-in function `pow`.  - Unlike the built-in function, `pow` from `math` returns only floats but not integers nor complex numbers. - We say that the import statement *polluted the namespace of the global frame* and caused a *name collision*.  **How to avoid name collisions?**<jupyter_code>%%mytutor -h 250
import math
print('{:.2f}, {:.2f}'.format(math.pow(-1, 2), pow(-1, 1 / 2)))<jupyter_output><empty_output><jupyter_text>We can use the full name (*fully-qualified name*) `math.pow` prefixed with the module name (and possibly package names containing the module). **Can we shorten a name?** The name of a library can be very long and there can be a hierarchical structure as well.  E.g., to plot a sequence using `pyplot` module from `matplotlib` package:<jupyter_code>%matplotlib inline
import matplotlib.pyplot
matplotlib.pyplot.stem([4, 3, 2, 1])
matplotlib.pyplot.ylabel(r'$x_n$')
matplotlib.pyplot.xlabel(r'$n$')
matplotlib.pyplot.title('A sequence of numbers')
matplotlib.pyplot.show()<jupyter_output><empty_output><jupyter_text>It is common to rename `matplotlib.pyplot` as `plt`:<jupyter_code>%matplotlib inline
import matplotlib.pyplot as plt
plt.stem([4, 3, 2, 1])
plt.ylabel(r'$x_n$')
plt.xlabel(r'$n$')
plt.title('A sequence of numbers')
plt.show()<jupyter_output><empty_output><jupyter_text>We can also rename a function as we import it to avoid name collision:<jupyter_code>from math import pow as fpow
fpow(2, 2), pow(2, 2)<jupyter_output><empty_output><jupyter_text>**Exercise** What is wrong with the following code?<jupyter_code>import math as m
for m in range(5): m.pow(m, 2)<jupyter_output><empty_output><jupyter_text>There is a name collision: `m` is assigned to an integer in the for loop and so it is no longer the module `math` when calling `m.pow`. **Exercise** Use the `randint` function from `random` to simulate the rolling of a die, by printing a random integer from 1 to 6.<jupyter_code>import random
print(random.randint(1, 6))<jupyter_output>5<jupyter_text>Built-in Functions **How to learn more about a function such as `randint`?** There is a built-in function `help` for showing the *docstring* (documentation string).<jupyter_code>import random
help(random.randint)  # random must be imported before
help(random)  # can also show the docstring of a module
help(help)<jupyter_output>Help on _Helper in module _sitebuiltins object:

class _Helper(builtins.object)
 |  Define the builtin 'help'.
 |  
 |  This is a wrapper around pydoc.help that provides a helpful message
 |  when 'help' is typed at the Python interactive prompt.
 |  
 |  Calling help() at the Python prompt starts an interactive help session.
 |  Calling help(thing) prints help for the python object 'thing'.
 |  
 |  Methods defined here:
 |  
 |  __call__(self, *args, **kwds)
 |      Call self as a function.
 |  
 |  __repr__(self)
 |      Return repr(self).
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)<jupyter_text>**Does built-in functions belong to a module?** Indeed, every function must come from a module.<jupyter_code>__builtin__.print('I am from the __builtin__ module.')<jupyter_output>I am from the __builtin__ module.<jupyter_text>`__builtin__` module is automatically loaded because it provides functions that are commonly use for all programs. **How to list everything in a module?**  We can use the built-in function `dir` (*directory*).<jupyter_code>dir(__builtin__)<jupyter_output><empty_output><jupyter_text>We can also call `dir` without arguments.  What does it print?<jupyter_code>dir()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11850849126261383, 'fraction_numerical': 0.015259660349495446, 'mean_word_length': 5.20381679389313, 'pattern_counts': {'":': 0, '<': 72, '<?xml version=': 0, '>': 73, 'https://': 6, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '19', 'dir': 'jupyter-structured-clean-dedup', 'id': '377535', 'license': 'CC0-1.0', 'n_tokens_mistral': 2590, 'n_tokens_neox': 2433, 'n_words': 917, 'path': 'Lecture4/Using Functions.ipynb', 'repo_name': 'ccha23/cs1302_20a', 'size': '8126'}