Due by 09/06/2016

Instructions

Download hw01.zip. Inside the archive, you will find a file called hw01.py, along with a copy of the OK autograder. Upload the zip file to Jupyter to complete the assignment. See Lab 0 for instructions on using Jupyter to complete assignments.

Submission: When you are done, submit the assignment by uploading the .py file to okpy.org. You may submit more than once before the deadline; only the final submission will be scored. See Lab 0 for instructions on submitting assignments.

Readings: This homework relies on following references:

Develop your understanding

Like getting to Carnegie Hall, becoming proficient in programming takes practice. The start of this assignment is intended to give you practice working with values and expressions while pointing out some of the subtleties that are good to have tucked away in your brain somewhere.

Please note that while those of you with some programming experience may find this to be a rather basic review, others may find it completely new. Either way, bear with us; we want everyone to have the basics.

Primitive expressions

A primitive expression requires only a single evaluation step: use the literal value directly. For example, numbers, names, and strings are all primitive expressions.

>>> 2
2
>>> 'Hello World!'
'Hello World!'

Arithmetic operations on integers

>>> 5 + 3
8
>>> 5 - 3
2
>>> 5 * 3  # comments look like this
15
>>> -47  # unary minus
-47
>>> 2 ** 3 # exponentiation
8

All of these operators took integers as inputs and produced integers as outputs. There are other operators that take integers as inputs and produce other data types, such as booleans or decimal numbers (called floats), as outputs.

>>> 2 == 1
False
>>> 5 - 3 == 2
True
>>> 5 / 3  # will this produce an integer?
1.6666666666666667
>>> 5 // 3 # here's another kind of division that has an integer result
1
>>> 5 % 3  # what's this crazy looking operation?
2

Division

Let's compare the different division-related operators in Python:

True Division
(decimal division)
The / Operator
Floor Division
(integer division)
The // Operator
Modulo
(similar to a remainder)
The % Operator
>>> 1 / 5
0.2

>>> 25 / 4
6.25

>>> 5 / 0
ZeroDivisionError
>>> 1 // 5
0

>>> 25 // 4
6

>>> 5 // 0
ZeroDivisionError
>>> 1 % 5
1

>>> 25 % 4
1

>>> 5 % 0
ZeroDivisionError

Note that floor division and modulo both return an integer while true division always returns a floating point number.

One useful technique involving the % operator is to check whether a number x is divisible by another number y:

x % y == 0

For example, in order to check if x is an even number:

x % 2 == 0

Floats and precedence

Floating point numbers (floats) behave a lot like real numbers. You can identify a float by the decimal point. All floats have decimal points. To write a floating point number (as a literal) you must add a decimal point!

>>> 3.141592
3.141592
>>> 2*3.141592 # you can mix ints and floats 
6.283184
>>> pie = 3.141592 # you can assign values to variables
>>> pie
3.141592
>>> pie/pie
1.0
>>> pie/pie == 1 # a float can be equal in value to an int
True
>>> from math import pi   # here is a better pi
>>> pi
3.141592653589793
>>> 5.0/3.0  # this is division of floats, not ints
1.6666666666666667
>>> 2**(1/2) # square root - isn't that transcendental?
1.4142135623730951

Expressions follow operator precedence (just like in math). Operations are performed one at a time in a specific order. Parenthesis are used to specify order (again, just like in math - remember PEMDAS?).

>>> 2 + 3 - 4 + 5   # equal precedence, left to right
6
>>> 2 + 3 - (4 + 5) # order matters - parentheses are your friend
-4
>>> (((2 + 3) - 4) + 5) # explicit order of the first example
6
>>> 2 + 3 * 4   # * and / bind more tightly than + or - 
14
>>> 2 + (3 * 4) 
14
>>> (2 + 3) * 4  
20
>>> 2 + 3 / 4 * 5  # what about * and / ?
5.75

An expression can have multiple return values, called a tuple:

>>> 2, 3
(2, 3)
>>> x, y = 1, pi
>>> y
3.141592653589793

Simple expressions

Question 1: What would Python print?

Use OK to test your knowledge with the some "What Would Python Print?" questions:

python ok -q primitive_expressions -u --local

Note: If you think any of the expressions would cause Python to throw an error, type "Error" (without quotes) when prompted.

Strings

It is very useful to be able to write programs that operate on strings, not just numbers. Without strings, web browsers and word processors would be like the matrix! Just as with ints, floats, and booleans, strings are a data type and have certain operators defined on them:

>>> 'cal'           # a string literal is a sequence of characters in quotes
'cal'
>>> "rocks"         # either kind of quote, but they need to match
'rocks'
>>> "cal" + "rocks"   # + is concatenation
'calrocks'
>>> 'cal' * 3         # * is replication
'calcalcal'
>>> 'cal' == "cal"  # equality is if they are the same string (notice the single and double quotes)
True
>>> 'Cal' == 'cal'  # case sensitive
False
>>> 'Cal' < 'cal'   # lexicographic ordering, with upper before lower case
True
>>> 'you' is not 'me'
True

Equality vs. comparison and Boolean operators

So far everything you've seen looks the same as the arithmetic you did in elementary school. But back then, without even knowing it you used the equals sign in two different ways: to test equality and to assign value to a variable. When programming we need to be a little more careful. When we want the operator that tests for equality, we use ==. Similarly, we have "not equal", "less than", "less than or equal", etc.:

>>> 2 == 3
False
>>> 2 != 3
True
>>> 2 < 2
False
>>> 2 <= 2
True

Equality should never be confused with assignment. To assign a value to a variable we use =:

>>> 2 = 3
SyntaxError: can't assign to literal
>>> two
NameError: name 'two' is not defined
>>> two = 2
>>> two
2
>>> two == 2
True
>>> two = 3     # don't let the name confuse you
>>> two
3
>>> two == 2
False
>>> x = 2 == 3  # This assigns a value to x.  Which?
>>> x
False

Please notice that the results above depended on the sequence of operations. We assigned the value 2 to the variable named two and then we later assigned the value 3 to this same variable.

We also have logic operators on booleans: and, or, and not:

>>> x = 2
>>> x > 1 or x != 0
True
>>> x == 2 and 3 > 4
False
>>> not x < 3
False

Python also has some handy operators that make expressions read more like prose:

>>> 2 is 1
False
>>> 2 is not 1
True

You briefly encountered Boolean operators last week, but here you will see there is a close connection between them and conditionals.

Python supports three boolean operators: and, or, and not:

>>> a = 4
>>> a < 2 and a > 0
False
>>> a < 2 or a > 0
True
>>> not (a > 0)
False
  • and evaluates to True only if both operands evaluate to True. If at least one operand is False, then and evaluates to False.
  • or evaluates to True if at least one operand evaluates to True. If all operands are False, then or evaluates to False.

What do you think the following expression evaluates to? Try it out in the Python interpreter.

>>> True and not False or not True and False

It is difficult to read complex expressions, like the one above, and understand how a program will behave. Using parentheses can make your code easier to understand. Just so you know, Python interprets that expression in the following way:

>>> (True and (not False)) or ((not True) and False)

This is because boolean operators, like arithmetic operators, have an order of operation:

  • not has the highest priority
  • and
  • or has the lowest priority

To make your code more readable, and and or work on more than just booleans (True, False). Other Python values can be considered "false-y," including 0, None, and '' (the empty string). All other values are considered "truth-y."

Question 2: WWPP: Truthiness

Use OK to test your knowledge with the following "What Would Python Print?" questions:

python ok -q truthiness -u --local

Short Circuiting

What do you think will happen if we type the following into Python?

1 / 0

Try it out in Python! You should see a ZeroDivisionError. But what about this expression?

True or 1 / 0

It evaluates to True because Python's and and or operators short-circuit. That is, they don't necessarily evaluate every operand.

Operator Evaluates from left to right until: Example
AND The first "false-y" value False and 1 / 0 evaluates to False
OR The first "truth-y" value True or 1 / 0 evaluates to True

If and and or do not short-circuit, they just return the last value. This means that and and or don't always return booleans when using truth-y and false-y values.

Here's a little example:

def divides(x, y):
    """
    return True if x divides y
    """
    return x and y % x == 0

Question 3: WWPP: Veritasiness

Use OK to test your knowledge with the following "What Would Python Print?" questions:

python ok -q short_circuiting -u --local

Hint: If an error occurs, write Error.

Question 4: Fix the Bug

The following snippet of code doesn't work! Figure out what is wrong and fix the bugs.

def both_positive(x, y):
    """Returns True if both x and y are positive.

    >>> both_positive(-1, 1)
    False
    >>> both_positive(1, 1)
    True
    """
    return x and y > 0 # You can replace this line!

Use OK to test your code:

python ok -q both_positive --local

Call expressions

All these operators are also functions. And, just as we build expressions with values, variables, and operators, we can also call functions.

A call expression applies a function, which may or may not accept arguments. The call expression evaluates to the function's return value.

The syntax of a function call:

  add   (    2   ,    3   )
   |         |        |
operator  operand  operand

Every call expression requires a set of parentheses delimiting its comma-separated operands.

To evaluate a function call:

  1. Evaluate the operator, and then the operands (from left to right).
  2. Apply the operator to the operands (the values of the operands).

If an operand is a nested call expression, then these two steps are applied to that operand in order to evaluate it.

Question 5: What Would Python Print?

Let's import the functions that correspond to the arithmetic operators you are already familiar with. (You can learn more here.) Don't worry too much about the import statement just yet. The fancy sounding idea is that these functions have names that reside in a namespace outside of our own. We can bring those names into our local namespace using an import statement.

Use OK to test your knowledge with the some "What Would Python Print?" questions:

python ok -q call_expressions -u --local

Now take a look at the following examples. Make sure you understand them before moving on!

>>> def welcome():
...     print('welcome to')
...     return 'hello'
...
>>> def cs88():
...     print('cs88')
...     return 'world'
...
>>> print(welcome(), cs88())
welcome to
cs88
hello world
>>> from operator import add
>>> def double(x):
...     return x + x
...
>>> def square(y):
...     return y * y
...
>>> def f(z):
...     add(square(double(z)), 1)
...
>>> f(4)
# Nothing shows up because the return value is None.
# How do you fix this?

If Statements

You can review the syntax and behavior of if statements in Section 1.5.4 of Composing Programs.

The conditional statement is a statement, not an expression; it does not return a value. The if-expression (or predicate) is evaluated first, before any other part of the statement, to determine whether to evaluate an arm. If the if-expression evaluates to a True value then the statement(s) following the : is evaluate. Otherwise, the else arm is evaluated, if present. Multiple predicates can be chained together with elif. They are evaluated sequentially. Often conditionals are often used along with return statements in functions. For example, in some census data you see in c8 you might want to decode the gender code.

def decode_gender(code):
    if (code == 0):
        return 'all'
    elif (code == 1):
        return 'male'
    elif (code == 2):
        return 'female'
    else:
        return 'unknown'

Conditionals are often used with assignment statements to simplify later expressions.

if ((year % 4) == 0) and (((year % 100) != 0) or ((year % 400) == 0)):
    year_len = 366
else:
    year_len = 365
<do something with year_len>

Or with print statements to control output

if (scene == 'architect skit'):
    print("spam, spam, spam")
else
    print("nobody expects the Spanish inquisition")

If statements are often misused by beginning programmers to accomplish what simple boolean expressions do much more naturally.

Hint: We sometimes see code that looks like this:

if x > 3:
    return True
else:
    return False

This can be written more concisely as return x > 3. If your code looks like the code above, see if you can rewrite it more clearly!

Question 6: WWPP: What If?

Use OK to test your knowledge with the following "What Would Python Print?" questions:

python ok -q what_if -u --local

The functions below are already defined in hw01.py. If you get stuck, try python -i hw01.py and experiment.

Hint: print (unlike return) does not cause the function to exit!

More Practice

Question 7: Add absolute

We've seen that we can name a value by assigning it to a variable. Functions are objects too. Try typing the name of a function you have defined or imported into the python interpreter.

Fill in the blanks in the following function definition for adding a to the absolute value of b, without calling abs or defining any new functions.

from operator import add, sub

def a_plus_abs_b(a, b):
    """Return a+abs(b), but without calling abs.

    >>> a_plus_abs_b(2, 3)
    5
    >>> a_plus_abs_b(2, -3)
    5
    """
    if b < 0:
        f = _____
    else:
        f = _____
    return f(a, b) # You can replace this line, but don't have to.

Use OK to test your code:

python ok -q a_plus_abs_b --local