Tips for navigating the slides:

- Press O or Escape for overview mode.
- Visit this link for a nice printable version
- Press the copy icon on the upper right of code blocks to copy the code

- Functional abstractions
- Naming things
- Debugging & errors

In CS, we often "abstract away the details":

We intentionally ignore some details
in order to provide a consistent interface.

In a world before functions...

```
interest = 1 + 0.6 * 2
interest2 = 1 + 0.9 * 4
interest3 = 1 + 2.1 * 3
```

Parameterized!

```
def interest(rate, years):
return 1 + rate * years
```

A **parameterized function** performs a computation that works for all acceptable
values of the parameters.

✂️ Removed detail: the values themselves!

A specification for the built-in `round`

function:

`round(number[, ndigits])`

: Return number rounded to `n`

digits precision after the decimal point.
If `n`

digits is omitted or is `None`

, it returns the nearest integer to its input.
See full documentation.

A well-designed **function specification** (function signature + docstring) serves as a contract between the implementer and the user.

✂️ Removed detail: the implementation!

Based on this specification..

`square(n)`

: Returns the square of the number `n`

.

This should work!

```
def sum_squares(x, y):
"""
>>> sum_squares(3, 9)
90
"""
return square(x) + square(y)
```

Many possible implementations can be used:

```
def square(x):
return pow(x, 2)
```

```
def square(x):
return x ** 2
```

```
from operator import mul
def square(x):
return mul(x, x)
```

```
square = lambda x: x * x
```

It could even be built-in to Python, in theory!

An implementation may have practical consequences:

- Affecting the size of the program
- Affecting the speed of the program's execution

Not the ideal implementation:

```
from operator import mul
def square(x):
return mul(x, x-1) + x
```

But you can cross that bridge 🌁 when you come to it.

There are only two hard things in Computer Science: cache invalidation and naming things. --Phil Karlton

Names typically don’t matter for correctness

but they matter a lot for readability.

From 😟 | To 🤩 |
---|---|

`true_false` |
`rolled_one` |

`d` |
`dice` |

`helper` |
`take_turn` |

`my_int` |
`num_rolls` |

Names should convey the meaning or purpose of the values to which they are bound.

Function names typically convey their effect
(`print`

), their behavior (`triple`

), or the
value returned (`abs`

).

The type of value bound to a parameter name is best documented in a function's docstring.

```
def summation(n, f):
"""Sums the result of applying the function F
to each term in the sequence from 1 to N.
N can be any integer > 1, F must take a single
integer argument and return a number.
"""
total = 0
k = 1
while k <= n:
total = total + f(k)
k = k + 1
return total
```

Repeated compound expressions:

```
if sqrt(square(a) + square(b)) > 1:
x = x + sqrt(square(a) + square(b))
```

⟱
```
hypotenuse = sqrt(square(a) + square(b))
if hypotenuse > 1:
x = x + hypotenuse
```

Meaningful parts of complex expressions:

```
x1 = (-b + sqrt(square(b) - 4 * a * c)) / (2 * a)
```

⟱
```
discriminant = square(b) - 4 * a * c
x1 = (-b + sqrt(discriminant)) / (2 * a)
```

Names can be short if they represent generic quantities: counts, arbitrary functions, arguments to mathematical operations, etc.

`n`

,`k`

,`i`

- Usually integers`x`

,`y`

,`z`

- Usually real numbers or coordinates`f`

,`g`

,`h`

- Usually functions

Names can be long if they help document your code:

```
average_age = average(age, students)
```

is preferable to...

```
# Compute average age of students
aa = avg(a, st)
```

These are common to all programming languages:

- Logic errors
- Syntax errors
- Runtime errors

A program has a logic error if it does not behave as expected. Typically discovered via failing tests or bug reports from users.

Spot the logic error:

```
# Sum up the numbers from 1 to 10
sum = 0
x = 1
while x < 10:
sum += x
x += 1
```

To avoid the wrath of angry users, write tests.

Each programming language has syntactic rules. If the rules aren't followed, the program cannot be parsed and will not be executed at all.

Spot the syntax errors:

```
if x > 5 # Missing colon
x += 1
```

```
sum = 0
x = 0
while x < 10:
sum + = x # No space needed between + and =
x + = 1
```

To fix a syntax error, read the message carefully and go through your code with a critical eye. 👁

`SyntaxError`

*What it technically means:*

The file you ran isn’t valid python syntax

*What it practically means:*

You made a typo

*What you should look for:*

- Extra or missing parenthesis
- Missing colon at the end of an if, while, def statements, etc.
- You started writing a statement but forgot to put any clauses inside

*Examples:*

```
print("just testing here"))
```

```
title = 'Hello, ' + name ', how are you?'
```

`IndentationError`

/`TabError`

*What it technically means:*

The file you ran isn't valid Python syntax, due to indentation inconsistency.

*What it sometimes means:*

You used the wrong text editor (or one with different settings)

*What you should look for:*

- A typo or misaligned block of statements
- A mix of tabs and spaces
- Open your file in an editor that shows them
`cat -A filename.py`

will show them

*Example:*

```
def sum(a, b):
total = a + b
return total
```

A runtime error happens *while* a program is running,
often halting the execution of the program.
Each programming language defines its own runtime errors.

Spot the runtime error:

```
def div_numbers(dividend, divisor):
return dividend/divisor
quot1 = div_numbers(10, 2)
quot2 = div_numbers(10, 1)
quot3 = div_numbers(10, 0) # Cannot divide by 0!
quot4 = div_numbers(10, -1)
```

To prevent runtime errors, code defensively and write tests for all edge cases.

`TypeError:'X' object is not callable`

*What it technically means:*

Objects of type X cannot be treated as functions

*What it practically means:*

You accidentally called a non-function as if it were a function

*What you should look for:*

- Parentheses after variables that aren't functions

*Example:*

```
sum = 2 + 2
sum(3, 5)
```

`...NoneType...`

*What it technically means:*

You used None in some operation it wasn't meant for

*What it practically means:*

You forgot a return statement in a function

*What you should look for:*

- Functions missing return statements
- Printing instead of returning a value

*Example:*

```
def sum(a, b):
print(a + b)
total = sum( sum(30, 45), sum(10, 15) )
```

`NameError`

*What it technically means:*

Python looked up a name but couldn't find it

*What it practically means:*

- You made a typo
- You are trying to access variables from the wrong frame

*What you should look for:*

- A typo in the name
- The variable being defined in a different frame than expected

*Example:*

```
fav_nut = 'pistachio'
best_chip = 'chocolate'
trail_mix = Fav_Nut + best__chip
```

`UnboundLocalError`

*What it technically means:*

A variable that's local to a frame was used before it was assigned

*What it practically means:*

You are trying to both use a variable from a parent frame,
and have the same variable be a local variable in the current frame

*What you should look for:*

Assignments statements after the variable name

*Example:*

```
sum = 0
def sum_nums(x, y):
sum += x + y
return sum
sum_nums(4, 5)
```

When there's a runtime error in your code,
you'll see a **traceback** in the console.

```
def div_numbers(dividend, divisor):
return dividend/divisor
quot1 = div_numbers(10, 2)
quot2 = div_numbers(10, 1)
quot3 = div_numbers(10, 0)
quot4 = div_numbers(10, -1)
```

```
Traceback (most recent call last):
File "main.py", line 14, in <module>
quot3 = div_numbers(10, 0)
File "main.py", line 10, in div_numbers
return dividend/divisor
ZeroDivisionError: division by zero
```

- The error message itself
- Lines #s on the way to the error
- What’s on those lines

The most recent line of code is always last (right before the error message).

```
Traceback (most recent call last):
File "main.py", line 14, in <module>
quot3 = div_numbers(10, 0)
File "main.py", line 10, in div_numbers
return dividend/divisor
ZeroDivisionError: division by zero
```

- Read the error message (remember what common error messages mean!)
- Look at each line, bottom to top, and see if you can find the error.

```
Traceback (most recent call last):
File "main.py", line 14, in <module>
quot3 = div_numbers(10, 0)
File "main.py", line 10, in div_numbers
return dividend/divisor
ZeroDivisionError: division by zero
```

```
def f(x):
return g(x - 1)
def g(y):
return abs(h(y) - h(1 /& y)
def h(z):
z * z
print(f(12))
```