This commit is contained in:
Daniel Bulant 2025-11-05 13:08:47 +01:00
parent ba56e467f0
commit c91fd07e47
16 changed files with 345 additions and 5 deletions

View file

@ -7,12 +7,16 @@
doc doc
} }
#let embedClass(name: str, label: none) = { #let embedClass(name: str, label: none, ..arguments) = {
show figure: set block(width: 100%) show figure: set block(width: 100%)
show figure: set align(left) show figure: set align(left)
show figure.caption: set align(center) 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 #label
] ]
} }

View file

@ -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) { for week in range(1, count + 1).filter(it => it != 8) {
let a = "./week" + str(week) + "/doc.typ" let a = "./week" + str(week) + "/doc.typ"
include a include a

View file

@ -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;
}
}

View file

@ -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;
}
}

27
week10/cars/Vehicle.java Normal file
View file

@ -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;
}
}

1
week10/common Symbolic link
View file

@ -0,0 +1 @@
../common

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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");
}
}

48
week10/doc.typ Normal file
View file

@ -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")

View file

@ -0,0 +1,16 @@
import java.util.Arrays;
import java.util.HashSet;
class CzechSpellchecker implements Spellchecker {
public HashSet<String> words = new HashSet<String>(
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;
}
}

View file

@ -0,0 +1,16 @@
import java.util.Arrays;
import java.util.HashSet;
class EnglishSpellchecker implements Spellchecker {
public HashSet<String> words = new HashSet<String>(
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;
}
}

View file

@ -0,0 +1,23 @@
import java.util.Arrays;
import java.util.HashSet;
class JavaSpellchecker implements Spellchecker {
public HashSet<String> words = new HashSet<String>(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;
};
}

View file

@ -0,0 +1,3 @@
interface Spellchecker {
boolean isWord(String word);
}

View file

@ -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 <lang> <...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");
}
}

View file

@ -4,8 +4,6 @@ import java.util.HashMap;
import java.util.regex.MatchResult; import java.util.regex.MatchResult;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import common.In;
public class UglierSoup { public class UglierSoup {
public static void main(String[] args) { public static void main(String[] args) {
// var in = new In("./test.html"); // var in = new In("./test.html");