diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index e7b2288c..9693c889 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -20,6 +20,10 @@ jobs: mkdir ~/files wget https://github.com/martyngigg/cpp-examples/raw/master/Holmes.txt -O ~/files/Holmes.txt + - name: Install numpy + run: | + pip install numpy + - name: Run Python tests run: | ls -lh ~/files diff --git a/exercises-python/mike_sullivan/README.md b/exercises-python/mike_sullivan/README.md new file mode 100644 index 00000000..9550d565 --- /dev/null +++ b/exercises-python/mike_sullivan/README.md @@ -0,0 +1,3 @@ +This directory contains the template python projects for each of the exercises. + +Do not write your code for the exercises in here. Instead, make a copy of the directory containing this file and name your new directory `firstname_surname`. The test files contain relative paths so the new directory should be at the same level as the parent `template` directory. diff --git a/exercises-python/mike_sullivan/ex01_basics/main.py b/exercises-python/mike_sullivan/ex01_basics/main.py new file mode 100644 index 00000000..8ed6175a --- /dev/null +++ b/exercises-python/mike_sullivan/ex01_basics/main.py @@ -0,0 +1,71 @@ +''' +Write a command line program that will: + +Take a filename of an ascii file as an argument (you can use the example file here) + +Load that ascii file. + +Count the number of occurrences of unique words (longer than 4 characters and split hyphenated words, treating each part as different words). It should be case and punctuation insensitive. You only need to consider the following punctuation characters .,?'"!(): (hint: you will need a backslash escape character for the double-quote) + +Consider handling of common error cases, such as the wrong file name specified. Return error and status information to the user of the command line tool. + +Print the results to screen showing the unique words and the number of uses in descending order of usage, e.g. + + +''' + +import argparse +import re +import numpy as np + +def get_user_arguments(): + # take in filename from user in command line + parser = argparse.ArgumentParser( + prog='top', + description='read ascii file') + parser.add_argument('filename', nargs='+') + # give option to show top number of lines + parser.add_argument('-l', '--lines', type=int) + args = parser.parse_args() + return args.filename[0], args.lines + +def read_file(filename): + f = open(filename, 'r') # 'r' = read + linesRaw = f.read() + # separate punctuation + lines = re.sub(r'(\W+)', lambda x: ' '+x.group(0)+' ', linesRaw).split() + #force all strings to be lowercase + lines = [i.lower() for i in lines] + # words longer than 4 characters + lines = [x for x in lines if len(x)>4] + f.close() + return lines + +def get_unique_words(lines): + values, counts = np.unique(lines, return_counts=True) + return values, counts + +def main(): + # Ask for filename from user + filename, numlines = get_user_arguments() + # Check if file exists + try: + lines = read_file(filename) + except FileNotFoundError: + print("File not found, please check file!") + exit() + # Cant find the file so exit the code + + # Sort the words by their frequency in the file + values, counts = get_unique_words(lines) + sortedValuesCounts = [x for x in sorted(zip(counts,values),reverse=True)] + + # print out words in descending order of frequency + print('Word Usage') + for usage, word in sortedValuesCounts[0:numlines]: + print(word, usage) + pass + + +if __name__ == "__main__": + main() diff --git a/exercises-python/mike_sullivan/ex02_oo_basics/main.py b/exercises-python/mike_sullivan/ex02_oo_basics/main.py new file mode 100644 index 00000000..9a1188f2 --- /dev/null +++ b/exercises-python/mike_sullivan/ex02_oo_basics/main.py @@ -0,0 +1,135 @@ +''' +Write a command line program that: + +Has classes to allow number of shapes to be defined: square (side1), rectangle(side1, side2), circle(radius), triangle(height, base). + +Each shape class should know it’s type (“Square”), how many sides it has. + +Each shape needs to be able to calculate it’s perimeter and area. For the triangle you can assume it is isoceles and the perimeter can be computed using +, where + is the base and + is the height. + +Within the Main method create a variety of the shapes and put them in a list + +Create a class ShapeSorter which should contain four methods + +Print out the Shapes that match a chosen type + +Print out the Shapes that match a chosen number of sides + +Print out the Shapes in order of area descending + +Print out the Shapes in order of perimeter descending +''' + +# Imports here +import numpy as np + +# Shape Classes here +class square(): + type = "Square" + number_sides = 4 + def __init__(self, side1): + self.side1 = side1 + self.Calc_perimeter() + self.Calc_area() + def printinfo(self): + print(f"This square has side {self.side1}, side number {self.number_sides}, perimeter {self.p}, area {self.a}") + def Calc_perimeter(self): + self.p = 4 * self.side1 + def Calc_area(self): + self.a = self.side1**2 + +class rectangle(): + type = "Rectangle" + number_sides = 4 + def __init__(self, side1, side2): + self.side1 = side1 + self.side2 = side2 + self.Calc_perimeter() + self.Calc_area() + def printinfo(self): + print(f"This rectangle has sides {self.side1} and {self.side2}, side number {self.number_sides}, perimeter {self.p}, area {self.a}") + def Calc_perimeter(self): + self.p = 2 * self.side1 + 2 * self.side2 + def Calc_area(self): + self.a = self.side1 * self.side2 + +class circle(): + type = "Circle" + number_sides = 1 + def __init__(self, radius): + self.radius = radius + self.Calc_perimeter() + self.Calc_area() + def printinfo(self): + print(f"This circle has radius {self.radius}, side number {self.number_sides}, perimeter {self.p}, area {self.a}") + def Calc_perimeter(self): + self.p = 2 * np.pi * self.radius + def Calc_area(self): + self.a = np.pi * self.radius**2 + +class triangle(): + type = "Triangle" + number_sides = 3 + def __init__(self, height, base): + self.height = height + self.base = base + self.Calc_perimeter() + self.Calc_area() + def printinfo(self): + print(f"This triangle has base {self.base} and height {self.height}, side number {self.number_sides}, perimeter {self.p}, area {self.a}") + def Calc_perimeter(self): + self.p = self.base + 2*np.sqrt(self.height**2 + (self.base**2 / 4)) + def Calc_area(self): + self.a = 1/2 * self.base * self.height + +class shapesorter(): + def __init__(self, ShapeList): + self.ShapeList = ShapeList + def PrintType(self, type): + print(f"Printing info on shapes with type: {type}") + for shape in self.ShapeList: + if shape.type == type: + shape.printinfo() + print("\n") + + def PrintNumSides(self, numsides): + print(f"Printing info on shapes with number of sides: {numsides}") + for shape in self.ShapeList: + if shape.number_sides == numsides: + shape.printinfo() + print("\n") + + def SortAreasDes(self): + print("Printing shapes with descending area:") + SortedShapeList = sorted(self.ShapeList, key=lambda x: x.a, reverse=True) + for shape in SortedShapeList: + shape.printinfo() + print("\n") + + def SortPerimeterDes(self): + print("Printing shapes with descending perimeter:") + SortedShapeList = sorted(self.ShapeList, key=lambda x: x.p, reverse=True) + for shape in SortedShapeList: + shape.printinfo() + print("\n") +def main(): + Triangle = triangle(2,6) + Circle1 = circle(3) + Circle2 = circle(12.4) + Square = square(3.4) + Rectangle1 = rectangle(2.4,6.3) + Rectangle2 = rectangle(1, 12) + + ShapeList = [Triangle, Circle1, Circle2, Square, Rectangle1, Rectangle2] + ShapeSorter = shapesorter(ShapeList) + ShapeSorter.PrintType("Circle") + ShapeSorter.PrintNumSides(4) + ShapeSorter.SortAreasDes() + ShapeSorter.SortPerimeterDes() + + +if __name__ == "__main__": + main()