From c91fd07e4760eb16aec6b4fe97342f6a19b4bd75 Mon Sep 17 00:00:00 2001 From: Daniel Bulant Date: Wed, 5 Nov 2025 13:08:47 +0100 Subject: [PATCH] week 10 --- common/common.typ | 8 ++- main.typ | 2 +- week10/cars/GasolineCar.java | 29 +++++++++ week10/cars/HybridCar.java | 39 +++++++++++++ week10/cars/Vehicle.java | 27 +++++++++ week10/common | 1 + week10/company/Employee.java | 22 +++++++ week10/company/Manager.java | 16 +++++ week10/company/Person.java | 30 ++++++++++ week10/doc.typ | 48 +++++++++++++++ week10/spellcheck/CzechSpellchecker.java | 16 +++++ week10/spellcheck/EnglishSpellchecker.java | 16 +++++ week10/spellcheck/JavaSpellchecker.java | 23 ++++++++ week10/spellcheck/Spellchecker.java | 3 + week10/spellcheck/SpellcheckerProgram.java | 68 ++++++++++++++++++++++ week9/UglierSoup.java | 2 - 16 files changed, 345 insertions(+), 5 deletions(-) create mode 100644 week10/cars/GasolineCar.java create mode 100644 week10/cars/HybridCar.java create mode 100644 week10/cars/Vehicle.java create mode 120000 week10/common create mode 100644 week10/company/Employee.java create mode 100644 week10/company/Manager.java create mode 100644 week10/company/Person.java create mode 100644 week10/doc.typ create mode 100644 week10/spellcheck/CzechSpellchecker.java create mode 100644 week10/spellcheck/EnglishSpellchecker.java create mode 100644 week10/spellcheck/JavaSpellchecker.java create mode 100644 week10/spellcheck/Spellchecker.java create mode 100644 week10/spellcheck/SpellcheckerProgram.java diff --git a/common/common.typ b/common/common.typ index 6873f6d..99287b5 100644 --- a/common/common.typ +++ b/common/common.typ @@ -7,12 +7,16 @@ doc } -#let embedClass(name: str, label: none) = { +#let embedClass(name: str, label: none, ..arguments) = { show figure: set block(width: 100%) show figure: set align(left) show figure.caption: set align(center) + let directory = arguments.named().at("directory", default: "") + let directory = if directory.len() == 0 { "" } else { directory + "/" } + let path = arguments.named().at("path", default: "../" + directory + name + ".java") + let kind = arguments.named().at("kind", default: "Class") [ - #figure(caption: name, kind: "Class", supplement: [Class], raw(read("../" + name + ".java"), lang:"java", block: true)) + #figure(caption: name, kind: kind, supplement: [#kind], raw(read(path), lang:"java", block: true)) #label ] } diff --git a/main.typ b/main.typ index e5ca3dd..b683a31 100644 --- a/main.typ +++ b/main.typ @@ -15,7 +15,7 @@ Collection of solutions to programming exercises as part of Introduction to Prog ) #{ - let count = 9; + let count = 10; for week in range(1, count + 1).filter(it => it != 8) { let a = "./week" + str(week) + "/doc.typ" include a diff --git a/week10/cars/GasolineCar.java b/week10/cars/GasolineCar.java new file mode 100644 index 0000000..e1abd49 --- /dev/null +++ b/week10/cars/GasolineCar.java @@ -0,0 +1,29 @@ +public class GasolineCar implements Vehicle { + /** liters */ + double fuel; + /** km per liter */ + final double mileage; + + /** + * @param mileage km per liter + */ + GasolineCar(double mileage) { + this.mileage = mileage; + } + + @Override + public int getRemainingRange() { + return (int)Math.floor(fuel * mileage); + } + + @Override + public int drive(int kms) { + var range = getRemainingRange(); + var toDrive = Math.min(range, kms); + // l = 1/(km/l) * km + var consumption = 1./mileage * toDrive; + fuel -= consumption; + + return toDrive; + } +} diff --git a/week10/cars/HybridCar.java b/week10/cars/HybridCar.java new file mode 100644 index 0000000..20d509f --- /dev/null +++ b/week10/cars/HybridCar.java @@ -0,0 +1,39 @@ +public class HybridCar implements Vehicle { + /** liters */ + double fuel; + /** km per liter */ + final double fuelMileage; + double electricalEnergy; + /** km per el energy point */ + final double electricityMileage; + + HybridCar(double fuelMileage, double electricityMileage) { + this.fuelMileage = fuelMileage; + this.electricityMileage = electricityMileage; + } + + @Override + public int getRemainingRange() { + return (int)(fuel * fuelMileage + electricalEnergy * electricityMileage); + } + + @Override + public int drive(int kms) { + // km + var range = getRemainingRange(); + // km + var toDrive = Math.min(kms, range); + // km + var rangeEl = (int)(electricalEnergy * electricityMileage); + // km + var toDriveEl = Math.min(toDrive, rangeEl); + // km + var toDriveGas = toDrive - toDriveEl; + // l = 1/(km/l) * km + var consumptionGas = 1./fuelMileage * toDriveGas; + var consumptionEl = 1./electricityMileage * toDriveEl; + fuel -= consumptionGas; + electricalEnergy -= consumptionEl; + return toDrive; + } +} diff --git a/week10/cars/Vehicle.java b/week10/cars/Vehicle.java new file mode 100644 index 0000000..422a4e4 --- /dev/null +++ b/week10/cars/Vehicle.java @@ -0,0 +1,27 @@ +interface Vehicle { + int getRemainingRange(); + int drive(int kms); + + public static void main(String[] args) { + // 100km / 5l + var gasCar = new GasolineCar(100. / 5.); + gasCar.fuel = 10; // 200km fuel + // 100km / 5l; 10 km / "energy unit" + var hybridCar = new HybridCar(100. / 5., 10.); + hybridCar.fuel = 5; // 100km fuel + hybridCar.electricalEnergy = 5; // 50km eletricity + + assert gasCar.getRemainingRange() == 200; + assert gasCar.drive(100) == 100; + assert gasCar.getRemainingRange() == 100; + assert gasCar.drive(200) == 100; + assert gasCar.getRemainingRange() == 0; + + assert hybridCar.getRemainingRange() == 150; + assert hybridCar.drive(50) == 50; + assert hybridCar.electricalEnergy == 0; + assert hybridCar.fuel == 5; + assert hybridCar.drive(200) == 100; + assert hybridCar.getRemainingRange() == 0; + } +} diff --git a/week10/common b/week10/common new file mode 120000 index 0000000..60d3b0a --- /dev/null +++ b/week10/common @@ -0,0 +1 @@ +../common \ No newline at end of file diff --git a/week10/company/Employee.java b/week10/company/Employee.java new file mode 100644 index 0000000..9cfec37 --- /dev/null +++ b/week10/company/Employee.java @@ -0,0 +1,22 @@ +public class Employee extends Person { + String jobTitle; + int salary; + + public Employee(String name, int age, String jobTitle, int salary) { + super(name, age); + this.jobTitle = jobTitle; + this.salary = salary; + } + + public String getJobTitle() { + return jobTitle; + } + + public int getSalary() { + return salary; + } + + public int getBaseSalary() { + return salary; + } +} diff --git a/week10/company/Manager.java b/week10/company/Manager.java new file mode 100644 index 0000000..6c33776 --- /dev/null +++ b/week10/company/Manager.java @@ -0,0 +1,16 @@ +public class Manager extends Employee { + int monthlyBonus; + + public Manager(String name, int age, String jobTitle, int salary, int monthlyBonus) { + super(name, age, jobTitle, salary); + this.monthlyBonus = monthlyBonus; + } + + public int getMonthlyBonus() { + return monthlyBonus; + } + + public int getSalary() { + return salary + monthlyBonus; + } +} diff --git a/week10/company/Person.java b/week10/company/Person.java new file mode 100644 index 0000000..806ee75 --- /dev/null +++ b/week10/company/Person.java @@ -0,0 +1,30 @@ +public class Person { + String name; + int age; + + Person(String name, int age) { + this.name = name; + this.age = age; + } + + public String getName() { + return name; + } + + public int getAge() { + return age; + } + + public static void main(String[] args) { + var person = new Person("P1", 20); + var employee = new Employee("P2", 40, "Senior OOP Architect", 100_000); + var manager = new Manager("P3", 40, "Product Manager", 130_000, 20_000); + + assert person.getAge() == person.age; + assert employee.getAge() == employee.age; + assert manager.getBaseSalary() == manager.salary; + assert manager.getSalary() > manager.getBaseSalary(); + assert employee.getBaseSalary() == employee.getSalary(); + assert manager.getJobTitle().equals("Product Manager"); + } +} diff --git a/week10/doc.typ b/week10/doc.typ new file mode 100644 index 0000000..2270282 --- /dev/null +++ b/week10/doc.typ @@ -0,0 +1,48 @@ +#import "./common/common.typ" : * + +#show: template + += Week 10 + +== Exercise 10.5 + +Write an interface to represent a spellchecker with a method `boolean isWord(String word)` that returns `true` if the given word is spelled correctly. Implement three classes for three different languages. + +- Each language implementation should recognize at least three words. +- The spellchecker must correctly handle both uppercase and lowercase letters. +- Add a `main` method that takes two command-line arguments: a language name and a string. The program should split the string into words and print any misspelled words. + +#embedClass(name: "Spellchecker", directory: "spellcheck", kind: "Interface") +#embedClass(name: "EnglishSpellchecker", directory: "spellcheck") +#embedClass(name: "CzechSpellchecker", directory: "spellcheck") +#embedClass(name: "JavaSpellchecker", directory: "spellcheck") +#embedClass(name: "SpellcheckerProgram", directory: "spellcheck") + +== Exercise 10.6 + +Write an interface to represent a vehicle with a method `int getRemainingRange()` that returns the number of kilometers the vehicle can drive with its current fuel. Implement two classes: a gasoline car and a hybrid car. + +- The gasoline car should store the amount of fuel left and its mileage (km per liter). +- The hybrid car should store both the amount of gasoline and electric energy left, along with the mileage for running on gasoline and electricity. +- The hybrid car's `getRemainingRange()` method should compute the total range by considering both gasoline and energy. +- Add an `int drive(int kms)` method that simulates driving the specified distance, depletes the fuel accordingly, and returns the actual number of kilometers driven. For the hybrid car, electricity is used before gasoline. +- Write a `main` method to test both vehicle implementations. + +#embedClass(name: "Vehicle", directory: "cars", kind: "Interface") +#embedClass(name: "GasolineCar", directory: "cars") +#embedClass(name: "HybridCar", directory: "cars") + +== Exercise 10.7 + +Write a class `Person` to represent a person with a name and an age. Create two subclasses: `Employee` (which extends `Person`) and `Manager` (which extends `Employee`). + +- The `Person` class should have a constructor that takes a name and an age. +- The `Employee` class should add a job title and a salary, with an appropriate constructor. +- The `Manager` class should add a monthly bonus field, with an appropriate constructor. +- Add appropriate getter methods for all fields in each class. +- Add a `getSalary()` method. Make sure `Manager` class takes the manager's monthly bonus into account. +- Write a `main` method to test the inheritance hierarchy by creating instances of each class. + +#embedClass(name: "Person", directory: "company") +#embedClass(name: "Employee", directory: "company") +#embedClass(name: "Manager", directory: "company") \ No newline at end of file diff --git a/week10/spellcheck/CzechSpellchecker.java b/week10/spellcheck/CzechSpellchecker.java new file mode 100644 index 0000000..4610127 --- /dev/null +++ b/week10/spellcheck/CzechSpellchecker.java @@ -0,0 +1,16 @@ +import java.util.Arrays; +import java.util.HashSet; + +class CzechSpellchecker implements Spellchecker { + public HashSet words = new HashSet( + Arrays.asList("tři", "tisíce", "sta", "třicet", "stříbrných", "stříkaček", "stříkalo", "přes", "střech")); + + public boolean isWord(String word) { + if (words.contains(word)) + return true; + var lowercasedFirst = Character.toLowerCase(word.charAt(0)) + word.substring(1); + if (words.contains(lowercasedFirst)) + return true; + return false; + } +} diff --git a/week10/spellcheck/EnglishSpellchecker.java b/week10/spellcheck/EnglishSpellchecker.java new file mode 100644 index 0000000..c98f6c6 --- /dev/null +++ b/week10/spellcheck/EnglishSpellchecker.java @@ -0,0 +1,16 @@ +import java.util.Arrays; +import java.util.HashSet; + +class EnglishSpellchecker implements Spellchecker { + public HashSet words = new HashSet( + Arrays.asList("buffalo", "I", "the", "a", "old", "young", "man", "woman", "boat")); + + public boolean isWord(String word) { + if (words.contains(word)) + return true; + var lowercasedFirst = Character.toLowerCase(word.charAt(0)) + word.substring(1); + if (words.contains(lowercasedFirst)) + return true; + return false; + } +} diff --git a/week10/spellcheck/JavaSpellchecker.java b/week10/spellcheck/JavaSpellchecker.java new file mode 100644 index 0000000..dc9fea9 --- /dev/null +++ b/week10/spellcheck/JavaSpellchecker.java @@ -0,0 +1,23 @@ +import java.util.Arrays; +import java.util.HashSet; + +class JavaSpellchecker implements Spellchecker { + public HashSet words = new HashSet(Arrays.asList("I", "Java", "Factory", "Extended")); + + public boolean isWord(String word) { + // we could use slices but they copy it char for char anyway + var buf = ""; + for (var i = 0; i < word.length(); i++) { + var ch = word.charAt(i); + if (Character.isUpperCase(ch)) { + if (!buf.isEmpty() && !words.contains(buf)) + return false; + buf = ""; + } + buf += ch; + } + if (!buf.isEmpty() && !words.contains(buf)) + return false; + return true; + }; +} \ No newline at end of file diff --git a/week10/spellcheck/Spellchecker.java b/week10/spellcheck/Spellchecker.java new file mode 100644 index 0000000..d9659c2 --- /dev/null +++ b/week10/spellcheck/Spellchecker.java @@ -0,0 +1,3 @@ +interface Spellchecker { + boolean isWord(String word); +} \ No newline at end of file diff --git a/week10/spellcheck/SpellcheckerProgram.java b/week10/spellcheck/SpellcheckerProgram.java new file mode 100644 index 0000000..5d47020 --- /dev/null +++ b/week10/spellcheck/SpellcheckerProgram.java @@ -0,0 +1,68 @@ +import java.util.Arrays; + +public class SpellcheckerProgram { + public static void main(String[] args) { + if (args.length < 2) { + assertTests(); + System.out.println("SpellcheckerProgram <...words>"); + return; + } + var lang = args[0]; + var otherArgs = Arrays.copyOfRange(args, 1, args.length); + var text = String.join(" ", otherArgs); + // in real / more advanced spell checker, this would be done per language + // capital letters aren't checked properly as the implementations don't know if + // the word starts a sentence or not + var words = splitWords(text); + + var spellchecker = getSpellchecker(lang); + + var failed = false; + for (var word : words) { + if (!spellchecker.isWord(word)) { + failed = true; + System.out.println(word); + } + } + + if (failed) { + System.exit(1); + } + } + + static String[] splitWords(String text) { + return text.split("[\\s.!?'\"]"); + } + + static Spellchecker getSpellchecker(String lang) { + return switch (lang) { + case "cs" -> new CzechSpellchecker(); + case "en" -> new EnglishSpellchecker(); + case "java" -> new JavaSpellchecker(); + default -> throw new Error("Unknown language"); + }; + } + + static boolean checkAllWords(Spellchecker spellchecker, String text) { + for (var word : splitWords(text)) { + if (!spellchecker.isWord(word)) { + return false; + } + } + return true; + } + + static void assertTests() { + var cs = getSpellchecker("cs"); + var en = getSpellchecker("en"); + var java = getSpellchecker("java"); + + assert checkAllWords(cs, + "tři tisíce tři třicet tři stříbrných stříkaček stříkalo přes tři tisíce tři třicet tři stříbrných střech."); + assert !checkAllWords(cs, "třitisíce"); + assert checkAllWords(en, "Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo"); + assert !checkAllWords(en, "Buffal buffal Buffal buffal buffal buffal Buffal buffal"); + assert checkAllWords(java, "IExtendedJavaFactoryFactory FactoryJavaFactoryExtended"); + assert !checkAllWords(java, "IExtendedJavaFactoryFactor"); + } +} diff --git a/week9/UglierSoup.java b/week9/UglierSoup.java index f1dc693..9bdde14 100644 --- a/week9/UglierSoup.java +++ b/week9/UglierSoup.java @@ -4,8 +4,6 @@ import java.util.HashMap; import java.util.regex.MatchResult; import java.util.regex.Pattern; -import common.In; - public class UglierSoup { public static void main(String[] args) { // var in = new In("./test.html");