Assignment 04

Solutions to Assignment 04.

Reverse Words

Write a function reverse_words that takes a sentence and returns a new sentence with all the words in the reserse order.

>>> reverse_words("joy of programming")
'programming of joy'

>>> reverse_words("less is more")
'more is less'

>>> reverse_words("road goes ever on and on")
'on and on ever goes road'

Please note that only the order of the words in the sentence is reversed, not the letters in each word.

Solution


def reverse_words(sentence):
    words = sentence.split()
    return " ".join(words[::-1])

Sequence of Numbers

Write a program seq.py that takes a number n as argument and prints numbers from 1 to n. It should support the following two flags.

-s START --start START
    print number from START to n instead of 1 to n

-r --reverse
    print the numbers in the reverse order

The program should also print approprate help message when used with -h or --help flags.

Use the standard library module argparse for doing this. You may want to checkout the argparse tutorial to know how to use that module.

Expected Output:

$ python seq.py 5
1
2
3
4
5

$ python seq.py -s 3 5
3
4
5

$ python seq.py -r -s 3 5
5
4
3

Solution

import argparse

p = argparse.ArgumentParser()
p.add_argument("n", type=int, help="the last number in the sequence")
p.add_argument("-s", "--start", type=int, default=1, help="the first number in the sequence (default: 1)")
p.add_argument("-r", "--reverse", action="store_true", default=False, help="display the numbers in reverse order")
args = p.parse_args()

numbers = list(range(args.start, args.n+1))
if args.reverse:
    numbers = numbers[::-1]

for n in numbers:
    print(n)

command tail

Write a python script tail.py which mimics unix command tail. The command will show last few lines of a file. It takes filename as argument and optional argument -n to specify how many lines to display. By default it displays last 5 lines of the file.

$ python tail.py --help
usage: tail.py [-h] [-n LINES] filename

positional arguments:
  filename              path to the file

options:
  -h, --help            show this help message and exit
  -n LINES, --lines LINES
                        Number of lines to display

$ python tail.py files/ten.txt
6
7
8
9
10

$ python tail.py -n 3 files/ten.txt
8
9
10

$ python tail.py zen.txt
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!

Solution

import argparse

p = argparse.ArgumentParser()
p.add_argument("filename", help="path to the file")
p.add_argument("-n", "--lines", type=int, default=5, help="Number of lines to display")

args = p.parse_args()
n = args.lines
lines = open(args.filename).readlines()
lines = lines[-n:]
for line in lines:
    print(line, end="")

Skip lines in a file

Write a program skip.py to print the contents of a file after skipping the first few lines.

The program takes an optional flag -n to indicate the number of lines to skip, which is considered as 5 when not specified.

The program takes a filename as argument and prints the contents of this file after skipping the number of lines specified by -n.

$ python skip.py files/ten.txt
6
7
8
9
10

$ python skip.py -n 8 files/ten.txt
9
10

Hint: Use argparse module.

Solution

import argparse

p = argparse.ArgumentParser()
p.add_argument("-n", type=int, default=5, help="number of lines to skip")
p.add_argument("filename", help="name of the file to consider")
args = p.parse_args()

lines = open(args.filename).readlines()

for line in lines[args.n:]:
    print(line, end="")

Split a File

Write a program split.py that splits a large file into multiple smaller files. The program should take a filename and the number of lines as arguments and write multiple small files each containing the specified number of lines (The last one may have smaller number of lines).

Suppose you have a file 100.txt in the current directory. (you can copy it from files/100.txt).

$ cp files/100.txt 100.txt

When you run split.py with that, it should split that into multiple small files.

$ python split.py 100.txt 30
writing 100-part1.txt
writing 100-part2.txt
writing 100-part3.txt
writing 100-part4.txt

Solution

"""Program to split a file into smaller parts.

It takes a filename and the number of lines in each part as
command-line arguments and splits the file into smaller parts
with each file having no more than the specified number of lines.

USAGE:
    $ python split.py large-file.txt 100
    writing large-file-part1.txt
    writing large-file-part2.txt
    ...
"""
import sys

def group(values, n):
    return [values[i:i+n] for i in range(0, len(values), n)]

def splitfile(filename, chunk_size):
    lines = open(filename).readlines()
    return group(lines, chunk_size)

def write_lines(filename, lines):
    """Write a list of lines to the a file.
    """
    print("writing", filename)
    with open(filename, "w") as f:
        f.writelines(lines)

def generate_part_filename(filename, index):
    """Generates a new filename by adding index as suffix to the filename.

        >>> generate_part_filename("a.txt", 1)
        "a-part1.txt"
    """
    nameparts = filename.split(".", 1)
    if len(nameparts) == 2:
        name, ext = nameparts
        ext = "." + ext
    else:
        name = filename
        ext = ""

    return f"{name}-part{index}{ext}"

def write_small_files(filename, file_chunks):
    for i, chunk in enumerate(file_chunks, start=1):
        new_filename = generate_part_filename(filename, i)
        write_lines(new_filename, chunk)

def main():
    filename = sys.argv[1]
    chunk_size = int(sys.argv[2])
    file_chunks = splitfile(filename, chunk_size)
    write_small_files(filename, file_chunks)

if __name__ == "__main__":
    main()