Homework 1
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 |
|
|
|
---|
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 toTrue
only if both operands evaluate toTrue
. If at least one operand isFalse
, thenand
evaluates toFalse
.or
evaluates toTrue
if at least one operand evaluates toTrue
. If all operands areFalse
, thenor
evaluates toFalse
.
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 priorityand
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:
- Evaluate the operator, and then the operands (from left to right).
- 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:
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