Range & Loop Problems

```          ```
def negative(n):
nums = []
for i in range(n, (-1 * n) - 1, -1):
nums.append(i)
return nums
# Note: unary - has the highest precedence, so the 2nd param
# can be written as -n - 1, but the parenthesis look fine too.

def threes(n):
outer = []
# first = first num of inner 1, 2, ... n
for first in range(1, n + 1):
inner = []
for i in range(first, first + 3):
inner.append(i)
outer.append(inner)
return outer

def countdown(n):
outer = []
# count = count of numbers in inner list
for count in range(1, n + 1):
inner = []
for i in range(10, 10 - count, -1):
inner.append(i)
outer.append(inner)
return outer
```
```

Finding the smallest unique positive integer

```          ```
def smallest_uniq_pos_int(filename):

all_positive_nums = []
seen_before = []
not_unique = []

with open(filename, 'r') as f:
for line in f:
num = int(line)
if num >= 1:
all_positive_nums.append(num)
if num in seen_before:
not_unique.append(num)
else:
seen_before.append(num)

smallest = 0
for num in all_positive_nums:
if num not in not_unique and (num < smallest or smallest == 0):
smallest = num

return smallest
```
```

Unique Elements in a List

```          ```
def get_unique(l):
output = []
for i in range(len(l)-1):
if l[i] not in l[:i] and l[i] not in  l[i+i:]:
output.append(l[i])
return output
```
```

String Parsing

Introduction to String Parsing

```              ```
def exclaim(s):
mark = s.find('!')
if mark == -1:
return ''
# scan left from the exclamation mark
i = mark - 1
while i >= 0 and s[i].isalpha():
i -= 1
word = s[i + 1: mark + 1]
if len(word) >= 2:
return word
return ''

def vowels(s):
colon = s.find(':')
if colon == -1:
return ''
# scan right from the colon
i = colon + 1
while i < len(s) and s[i].lower() in 'aeiou':
i += 1
word = s[colon + 1:i]
return word
```
```

Extracting Email Hostnames

This is one possible approach to decomposing this problem. Think about other ways to structure your solution!

```              ```
def extract_hostname(line):
at_index = line.find('@')
if at_index == -1:
return ''
# scan forward till the end of the word or line
i = at_index + 1
while i < len(line) and line[i] != ' ':
if line[i].isalpha() or line[i] =='.':
i += 1
else:
break # end the loop immediately - we'll talk about this more next week!

hostname = line[at_index + 1: i]

if len(hostname) < 4 or not '.' in hostname:
return ''

return hostname

def extract_all_hostnames(filename):
hostnames = []
with open(filename, 'r') as f:
for line in f:
hostname = extract_hostname(line)
if hostname != '' and not hostname in hostnames:
hostnames.append(hostname)
hostnames = sorted(hostnames)
return hostnames
```
```

A much better email parser

This is the solution to parts 1 and 2 of this problem.

```          ```
import sys

def is_email_char(ch):
return ch.isalnum() or ch in ['.', '-', '_']

def parse_emails(s, max_per_line, permitted_host):
emails = []
search = 0
while (max_per_line == -1 or len(emails) < max_per_line) and search < len(s):
at = s.find('@', search)
if at == -1:
break
start = at - 1
while start >= 0 and is_email_char(s[start]):
start -= 1
end = at + 1
while end < len(s) and is_email_char(s[end]):
end += 1
host = s[at+1:end]
if permitted_host == '' or host == permitted_host:
email = s[start+1:end]
if host.find(".") != -1 and at - start > 0:
emails.append(email)
search = end
return emails

def parse_all_emails(filename, max_per_line, permitted_host):
parsed = []
with open(filename, 'r') as f:
for line in f:
parsed.extend(parse_emails(line, max_per_line, permitted_host))
return parsed

def main():
args = sys.argv[1:]
if len(args) == 1:
print(parse_all_emails(args[0], -1, ''))
else:
if args[0] == '-max':
max_per_line = int(args[1])
parse_all_emails(args[2], max_per_line, '')
else:
permitted_host = args[1]
parse_all_emails(args[2], -1, permitted_host)

if __name__ == "__main__":
main()
```
```

Calculating Compatibilities

```          ```
def in_common(l1, l2):
num_in_common = 0
total_elem = len(l1) + len(l2)
for i in range(len(l1)):
if l1[i] in l2:
num_in_common += 1

return num_in_common / total_elem

def calc_score(netflix_history1, netflix_history2, fav_books1, fav_books2):
netflix_score = in_common(netflix_history1, netflix_history2)
book_score = in_common(fav_books1, fav_books2)
return netflix_score + book_score

def new_friend(name_list, compatability_scores):
best_score = -1
best_friend = None

for i in range(len(compatability_scores)):
compatability_score = compatability_scores[i]
if compatability_score > best_score: # we could make this >= as well
best_score = compatability_score
best_friend = name_list[i]
```
```

The `main` function

This is one possible approach to decomposing this problem. Think about other ways to structure your solution!

```            ```
"""
calculator.py
-------------
Implements the calculator program, as specified in the
section 4 handout.
"""
import sys

def exp(base, power):
total = 1
for i in range(power):
total *= base

def square(base):
return exp(base, 2)

total = 0
for i in range(len(args) - 2):
total += int(args[i + 2])

def main():
args = sys.argv[1:]

if operation == "-exp":
base = int(args[2]) # convert the argument to an int
power = int(args[3])
result = exp(base, power)
print(result)

if operation == "-square":
base = int(args[2])
result = square(base)
print(result)

if operation == "-add":