Sometimes, you can append certain "flags" on the command line to
inspect your code further. Here are a few useful ones that'll come in
handy this semester. If you want to learn more about other python
flags, you can type `man python`

**no flags:**Adding no flags will directly run your Python script, meaning that Python will run the code in the file you provide and return you to the command line.`python3 FILE_NAME`

**-i**: The*-i*option runs your Python script, and throws you into an interactive session. If you omit the -i option, Python will only run your script. See the next section regarding interactive sessions to learn more!`python3 -i FILE_NAME`

**-m doctest**: Using*-m doctest*option will be useful on your homeworks and projects to help you test your code by showing you whether your code is working as you intend it to. Doctests are marked by triple quotations (""") and are usually located within the function.`python3 -m doctest FILE_NAME`

**-v**: The*-v*option signifies a verbose option. You can append this flag to the*-m doctest*flag to show both passing and failing tests. With the*-v*flag, you will be notified of all results (both failing and passing tests).`python3 -m doctest -v FILE_NAME`

Sometimes, you just want to try some things out in the python
interpreter. If you want to test out functions in a file, you'll need
the `-i`

flag as we specified above.

However, if you just need to try something out in the interpreter, without any user defined functions this is how you start an interactive session:

```
python3
```

On Cygwin:

```
python3 -i
```

Predict what Python will print in response to each of these
expressions. Then try it and make sure your answer was correct, or if
not, that you understand why! Remember, to start Python, type `python3`

into the command line.

```
>>> a = 4
>>> b = a
>>> a = 10
>>> a == b
______________
>>> z, y = 1, 2
>>> print(z)
______________
>>> def square(x):
... print(x * x) # Hit enter twice
...
>>> a = square(2)
______________
>>> print(a)
______________
>>> def square(y):
... return y * y # Hit enter twice
...
>>> a = square(2)
>>> print(a)
_______________
```

What would Python print? Try to figure it out before you type it into the interpreter!

```
# Q1
>>> a, b = 10, 6
>>> a > b and a == 0
_______________
# Q2
>>> a > b or a == 0
_______________
# Q3
>>> not a > 0
_______________
# Q4
>>> a != 0
_______________
# Q5
>>> True and False
_______________
# Q6
>>> True or False
_______________
# Q7
>>> not True and False
_______________
# Q8
>>> not (True and False)
_______________
# Q9
>>> False or False
_______________
# Q10
>>> True and True or True and False
_______________
```

Just like mathematical operators, boolean operators (`and`

, `or`

,
and `not`

) have an order of operations:

`not`

(highest priority)`and`

`or`

(lowest priority)

For example, the following expression will evaluate to `True`

:

```
True and not False or not True and False
```

It might be easier to rewrite the expression like this:

```
(True and (not False)) or ((not True) and False)
```

If you find writing parentheses to be clearer, it is perfectly acceptable to do so in your code.

In Python, `and`

and `or`

are examples of *short-circuiting operators*.
Consider the following code:

```
10 > 3 or 1 / 0 != 1
```

Generally, operands are evaluated from left to right in Python. The
expression `10 > 3`

will be evaluated first, then `1 / 0 != 1`

will be
evaluated. The problem is, evaluating `1 / 0`

will cause Python to
raise an error, stopping evaluation altogether! (You can try dividing
by 0 in the interpreter).

However, the original line of code will not cause any errors -- in
fact, it will evaluate to `True`

. This is made possible due to
short-circuiting, which works in the following ways:

`and`

will evaluate to`True`

only if*all*the operands are`True`

. For multiple`and`

statements, Python will go left to right until it runs into the first`False`

value -- then it will just immediately evaluate to`False`

.`or`

will evaluate to`True`

if*at least one*of the operands is`True`

. For multiple`or`

statements, Python will go left to right until it runs into the first`True`

value -- then it will immediately evaluate to`True`

.

Some examples:

```
>>> True and False and 1 / 0 == 1 # stops at the False
False
>>> True and 1 / 0 == 1 and False # hits the division by zero
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
>>> True or 1 / 0 == 1 # stops at the True
True
>>> False or 1 / 0 == 1 or True # hits the division by zero
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
```

What would the Python interpreter display?

```
>>> a, b = 10, 6
>>> if a == b:
... a
... else:
... b
...
_______________
>>> if a == 4:
... 6
... elif b >= 4:
... 6 + 7 + a
... else:
... 25
...
________________
```

Using

`=`

instead of`==`

: remember,`=`

(single equals) is used for*assignment*, while`==`

(double equals) is used for*comparison*.`# bad >>> if a = b: ... print("uh oh!") ... # good! >>> if a == b: ... print("yay!") ...`

Multiple comparisons: for example, trying to check if both

`x`

and`y`

are greater than 0.`# bad >>> if x and y > 0: ... print("uh oh!") # good! >>> if x > 0 and y > 0: ... print("yay!")`

`else`

Consider the following function:

```
>>> def abs(x):
... if x >= 0:
... return x
... else:
... return -x
```

It is syntactically correct to rewrite `abs`

in the following way:

```
>>> def abs(x):
... if x >= 0:
... return x
... return -x # missing else statement!
```

This is a direct consequence of how `return`

works -- when
Python sees a `return`

statement, it will *immediately terminate* the
function, and the rest of the function will not be evaluated. In the
above example, if `x >= 0`

, Python will never reach the final line.
Try to convince yourself that this is indeed the case before moving on.

Keep in mind that **omitting the else only works if the function is
terminated**! For example, the following function will

`if`

suite:```
>>> def foo(x):
... if x > 0:
... print("greater than zero")
... print("less than zero")
...
>>> foo(-3)
less than zero
>>> foo(4)
greater than zero
less than zero
```

In general, omitting the `else`

will make your code more concise --
however, if you find that it makes your code harder to read, by all
means use an `else`

statement.

What would Python print?

```
>>> n = 2
>>> def exp_decay(n):
... if n % 2 != 0:
... return
... while n > 0:
... print(n)
... n = n // 2
...
>>> exp_decay(1024)
__________________
>>> exp_decay(5)
__________________
>>> def funky(k):
... while k < 50:
... if k % 2 == 0:
... k += 13
... else:
... k += 1
... print(k)
... return k
>>> funky(25)
__________________
>>> n, i = 7, 0
>>> while i < n:
... i += 2
... print(i)
__________________
>>> n = 3
>>> while n > 0:
... n -= 1
... print(n)
__________________
>>> n = 3
>>> while n >= 0:
... n -= 1
... print(n)
__________________
>>> # typing Ctrl-C will stop infinite loops
>>> n = 4
>>> while True:
... n -= 1
... print(n)
__________________
```

Before we write our next function, let's compare the ideas of true
division (single slash `/`

in Python; does decimal division), floor
division (double slash `//`

in Python; rounds down to the nearest
integer), and modulo (percent sign `%`

in Python; similar to a
remainder):

True Division (decimal division):

`>>> 1 / 4 0.25 >>> 4 / 2 2.0 >>> 5/3 1.666666666667`

Floor Division (integer division):

`>>> 1 // 4 0 >>> 4 // 2 2 >>> 5 // 3 1`

Modulo (similar to a remainder):

`>>> 1 % 4 1 >>> 4 % 2 0 >>> 5 % 3 2`

Given the operators above, the following relationship holds:

```
b * (a // b) + (a % b) = a
```

Now, define a function `factors(n)`

which takes in a number, n, and
prints out all of the numbers that divide `n`

evenly. For example, a
call with `n = 20`

should result as follows (order doesnâ€™t matter):

```
>>> factors(20)
20
10
5
4
2
1
```

*Hint*: One common use of the `%`

operator is to find if something
divides evenly into a number. For example:

```
>>> 10 % 5
0
>>> 10 % 4
2
>>> 10 % 7
3
>>> 10 % 2
0
```

By now, you've probably seen a couple of error messages. Even though they might look intimidating, error messages are actually very helpful in debugging code. The following are some common error messages (found at the bottom of a traceback):

**SyntaxError**: Indicates that your code contains improper syntax (e.g. missing a colon after an`if`

statement).**IndentationError**: Indicates that your code contains improper indentation (e.g. inconsistent indentation of a function body)**TypeError**: Indicates an attempted operation on incompatible types (e.g. trying to add a function and an int)**ZeroDivisionError**: Indicates an attempted division by zero.

Using these descriptions of error messages, you should be able to get
a better idea of what went wrong with your code. **If you run into
error messages, try to identify the problem before asking for
help.** You can often Google unknown error messages to see what
similar mistakes others have made to help you debug your own code.

Here's a link to a helpful Debugging Guide written by Albert Wu.

Higher order functions are functions that take a function as an input, and/or output a function. We will be exploring many applications of higher order functions.

For each question, try to determine what Python would print. Then check in the interactive interpreter to see if you got the right answer.

```
>>> def square(x):
... return x*x
>>> def neg(f, x):
... return -f(x)
# Q1
>>> neg(square, 4)
_______________
>>> def first(x):
... x += 8
... def second(y):
... print('second')
... return x + y
... print('first')
... return second
...
# Q2
>>> f = first(15)
_______________
# Q3
>>> f(16)
_______________
>>> def foo(x):
... def bar(y):
... return x + y
... return bar
>>> boom = foo(23)
# Q4
>>> boom(42)
_______________
# Q5
>>> foo(6)(7)
_______________
>>> func = boom
# Q6
>>> func is boom
_______________
>>> func = foo(23)
# Q7
>>> func is boom
_______________
>>> def troy():
... abed = 0
... while abed < 10:
... def britta():
... return abed
... abed += 1
... abed = 20
... return britta
...
>>> annie = troy()
>>> def shirley():
... return annie
>>> pierce = shirley()
# Q8
>>> pierce()
________________
```

If you haven't found this gem already, tutor.composingprograms.com has a great visualization tool for environment diagrams. Post in your python code and it will generate an environment diagram you can walk through step-by-step! Use it to help you check your answers!

Try drawing environment diagrams for the following examples and predicting what Python will output:

```
# Q1
def square(x):
return x * x
def double(x):
return x + x
a = square(double(4))
# Q2
x, y = 4, 3
def reassign(arg1, arg2):
x = arg1
y = arg2
reassign(5, 6)
# Q3
def f(x):
f(x)
print, f = f, print
a = f(4)
b = print(4)
# Q4
def adder_maker(x):
def adder(y):
return x + y
return adder
add3 = adder_maker(3)
add3(4)
sub5 = adder_maker(-5)
sub5(6)
sub5(10) == add3(2)
```

Define a function `cycle`

which takes in three functions as arguments:
`f1`

, `f2`

, `f3`

. `cycle`

will then return another function. The
returned function should take in an integer argument `n`

and do the
following:

- Return a function that takes in an argument
`x`

and does the following:- if
`n`

is 0, just return`x`

- if
`n`

is 1, apply the first function that is passed to`cycle`

to`x`

- if
`n`

is 2, the first function passed to`cycle`

is applied to`x`

, and then the second function passed to cycle is applied to the result of that (i.e.`f2(f1(x))`

) - if
`n`

is 3, apply the first, then the second, then the third function (i.e.`3(f2(f1(x)))`

) - if
`n`

is 4, apply the first, then the second, then the third, then the first function (i.e.`f1(f3(f2(f1(x))))`

) - And so forth.

- if

*Hint*: most of the work goes inside the most nested function.

```
def cycle(f1, f2, f3):
""" Returns a function that is itself a higher order function
>>> def add1(x):
... return x + 1
...
>>> def times2(x):
... return x * 2
...
>>> def add3(x):
... return x + 3
...
>>> my_cycle = cycle(add1, times2, add3)
>>> identity = my_cycle(0)
>>> identity(5)
5
>>> add_one_then_double = my_cycle(2)
>>> add_one_then_double(1)
4
>>> do_all_functions = my_cycle(3)
>>> do_all_functions(2)
9
>>> do_more_than_a_cycle = my_cycle(4)
>>> do_more_than_a_cycle(2)
10
>>> do_two_cycles = my_cycle(6)
>>> do_two_cycles(1)
19
"""
"*** YOUR CODE HERE ***"
```