Assignment 02

Solutions to Assignment 02.

Despace

Write a function despace to remove all the spaces from a string.

>>> despace("hello world")
'helloworld'
>>> despace("a b c d e")
'abcde'
>>> despace("pi seconds is a nano century")
'pisecondsisananocentury'

Solution

def despace(text):
    return text.replace(" ", "")

Sort Words

Write a function sort_words to rearrange the words in a sentense in the sorted or order.

>>> sort_words("one two three four five")
'five four one three two'
>>> sort_words("white cat and black dog")
'and black cat dog white'
>>> sort_words("have a nice day")
'a day have nice'

Solution

def sort_words(sentence):
    words = sentence.split()
    return " ".join(sorted(words))

Minimum of Three Numbers

Write a function minimum3 to compute the minimum of three numbers.

You are not allowed to use any built-in functions in this implementation.

>>> minimum3(1, 2, 3)
1
>>> minimum3(3, 2, 1)
1
>>> minimum3(1, 1, 1)
1

Solution

def minimum(a, b):
    if a < b:
        return a
    else:
        return b

def minimum3(a, b, c):
    return minimum(minimum(a, b), c)

Longest Line

Write a program longest_line.py to print the longest line from a file.

The program should take a filename as a command-line argument and print the longest line from it.

$ python longest_line.py files/five.txt
three

$ python longest_line.py files/zen-of-python.txt
There should be one-- and preferably only one --obvious way to do it.

$ python longest_line.py files/bumper-stickers.txt
The first 90 percent of the code accounts for the first 90 percent of the development time. The remaining 10 percent of the code accounts for the other 90 percent of the development time.

Solution

import sys
path = sys.argv[1]

lines = open(path).readlines()
longest = max(lines, key=len)
print(longest, end="")

Line with Most Words

Write a program line_with_most_words.py that takes a filename as command-line argument and prints the line with the most number of words from the file.

$ cat files/words.txt
one
one two
one two three
one two three four
one two three four five
two three four five
three four five
four five
five
one-two-three-four-five-six-seven

$ python line_with_most_words.py files/words.txt
one two three four five

Solution

import sys
filename = sys.argv[1]
lines = open(filename).readlines()

def word_count(line):
    return len(line.split())

longest = max(lines, key=word_count)
print(longest.strip("\n"))

List Files

Write a program ls.py to list files in a directory.

The program should take path to a directory as command-line argument and print all the files (and sub-directories) in that directory.

$ python ls.py files/abc
a.txt
b.txt
c.txt

Solution

import sys
import os

path = sys.argv[1]
for f in os.listdir(path):
    print(f)

Sum File

Write a program sumfile.py that takes a filename as argument and prints sum of all numbers in the file. It is assumed that the file contains one number per line.

$ python sumfile.py files/ten.txt
55

Solution

import sys
filename = sys.argv[1]
numbers = [int(line) for line in open(filename)]
print(sum(numbers))

Grep Command

Implement Unix command grep in Python.

Write a program grep.py that takes a pattern and a file as command-line arguments and print all the lines in the file that contain that pattern.

The pattern could be any text and there is no need to support regular expressions.

$ cat files/zen-of-python.txt
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

$ python grep.py never files/zen-of-python.txt
Errors should never pass silently.
Now is better than never.
Although never is often better than *right* now.

$ grep the files/zen-of-python.txt
Special cases aren't special enough to break the rules.
In the face of ambiguity, refuse the temptation to guess.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.

Solution

import sys

pat = sys.argv[1]
filename = sys.argv[2]

for line in open(filename):
    if pat in line:
        print(line, end="")

Cumulative Sum

Cumulative sum of a list [a, b, c, ...] is defined as [a, a+b, a+b+c, ...].

Write a function cumulative_sum to compute the cumulative sum of a list of numbers.

>>> cumulative_sum([1, 2, 3, 4])
[1, 3, 6, 10]
>>> cumulative_sum([4, 3, 2, 1])
[4, 7, 9, 10]

Solution


def cumulative_sum(numbers):
    result = []
    csum = 0
    for n in numbers:
        csum += n
        result.append(csum)
    return result

Count Increased

Given a list of numbers, count the number of times a number in the list increases from the previous number.

For example:

Given the list of numbers as [3, 5, 4, 9, 2, 8]

3 (N/A - no previous number)
5 (increased)
4 (decreased)
9 (increased)
2 (decreased)
8 (increased)

So the number of times a number increased from the previous number is 3.

Write a function count_increased that takes a list of numbers as arguments and returns the number of times a number in that list is increased from the previous one.

>>> count_increased([3, 5, 4, 9, 2, 8])
3

Credits: this problem is modelled after Day 1 problem of Advent of Code 2021.

Solution

def count_increased(numbers):
    return sum([a > b for a, b in zip(numbers[1:], numbers)])

Discussion

Let’s consider the example carefully and try to solve it by hand.

3 5 4 9 2 8

How do we find if a number is more than it’s previous number?

We could repeats the numbers in two rows and shift the first one to right by one position and see if each number is increased from the above number.

- 3 5 4 9 2 8
3 5 4 9 2 8 -
--------------
  1 0 1 0 1

As you can see there are three cases where a number is increased from its previous number.

A naive approach to do this with code, would be to go over the numbers using index and see if current number is bigger than the one at the previous index.

def count_increased(numbers):
    count = 0
    for i in range(1, len(numbers)):
        prev = numbers[i-1]
        current = numbers[i]

        if current > prev:
            count += 1
    return count

There are a couple of issues with this approach.

  1. Accessing elements using index feels too low-level in Python
  2. Updating the count in the loop is tedius. There must be a better way.

Lets see if we can align the numbers in the list with their previous values.

numbers = [3, 5, 4, 9, 2, 8]
print(numbers[1:])
print(numbers)
[5, 4, 9, 2, 8]
[3, 5, 4, 9, 2, 8]

As you can see, we have managed to align the numbers with their previous ones.

Now we could use the zip function to put the pairs together.

pairs = zip(numbers[1:], numbers)
list(pairs)
[(5, 3), (4, 5), (9, 4), (2, 9), (8, 2)]
for a, b in zip(numbers[1:], numbers):
    print(a, b, a > b)
5 3 True
4 5 False
9 4 True
2 9 False
8 2 True

As you can see there are three increases.

We could write a list comprehension to get just the True/False values.

[a > b for a, b in zip(numbers[1:], numbers)]
[True, False, True, False, True]

In Python, True is eqvivalant of 1 and False is 0. So we could just sum the result to get the number of True values.

sum([a > b for a, b in zip(numbers[1:], numbers)])
3

Let’s put in a function now.

def count_increased(numbers):
    return sum([a > b for a, b in zip(numbers[1:], numbers)])