LMU ☀️ CMSI 3801
LANGUAGES AND AUTOMATA I
HOMEWORK #4 Due: 2022-11-03

Learning Objectives

With this assignment you will demonstrate:

Readings

Please read:

Instructions

You will be adding to the private GitHub repository for this course. After adding and modifying files, your repository will look like this:

  .
  ├── README.md
  ├── javascript/
  │   └── (existing files from previous assignments)
  ├── python/
  │   └── (existing files from previous assignments)
  ├── java/
  │   └── (existing files from previous assignments)
  └── swift/
      ├── .gitignore       (You are on your own to figure out what goes here)
      ├── exercises.swift
      └── main.swift       (The tests, given to you below)

Submit to BrightSpace a link to your repo. Make sure your README has the names of all the students that have worked on the project. Please submit only one solution set for your team. It does not matter under whose GitHub account your work is stored. Make sure both Julian and I are added as contributors to your repo.

You will be graded on your programming style, so make sure you are set up so that your editor or IDE auto-formats your code. Use plugins for your editor that include formatter and perhaps a linter.

Your homework submission will be the state of your repository on the main branch, at the due datetime, which is 11:59 p.m. on the due date in the America/Los Angeles time zone.

Project Setup

There’s nothing special here; just drop the source and test files in the same folder as indicated in the repository layout above. Professional Swift programmers set up and test real projects according to certain conventions, but at this point in the class, just focus on learning the language. You can learn the ecosystem on your own.

Your job is to write a single module with several functions and classes, and to add whatever lines are appropriate to your .gitignore file(s). The functions are described below. For unit tests, use this assert-filled program, provided free of charge, because you are special:

main.swift
import Foundation

// First check ensures you defined your own struct NegativeAmountError.
// If you implement your solution correctly, you will get a warning 
// when you compile, that's ok!
assert(change(200) is Result<(Int, Int, Int, Int), NegativeAmountError>)
switch change(250) {
    case .success(let coins): assert(coins == (10, 0, 0, 0))
    case _: assert(false)
}
switch change(-50) {
    case .success(_): assert(false)
    case .failure(let error): assert(error is NegativeAmountError)
}
assert(try! change(0).get() == (0, 0, 0, 0))
assert(try! change(97).get() == (3, 2, 0, 2))
assert(try! change(8).get() == (0, 0, 1, 3))
assert(try! change(144).get() == (5, 1, 1, 4))
assert(try! change(97).get() == (3, 2, 0, 2))
assert(try! change(100000000000).get() == (4000000000, 0, 0, 0))
assert((try? change(-50).get()) == nil)

assert("".stretched == "")
assert("  ".stretched == "")
assert("  \t\n  \t".stretched == "")
assert("  Hi  hi  ".stretched == "Hiihhhiiii")
assert("😁😂😱".stretched == "😁😂😂😱😱😱")
assert("hello world".stretched ==
    "heelllllllooooowwwwwwooooooorrrrrrrrllllllllldddddddddd")
assert("😁👩🏽‍🎤🧑🏻‍🔧".stretched == "😁👩🏽‍🎤👩🏽‍🎤🧑🏻‍🔧🧑🏻‍🔧🧑🏻‍🔧")

assert([].mapThenUnique { Int($0) * $0 } == Set([]))
assert([2, 9, -9, 3].mapThenUnique { Int($0) * $0 } == Set([4, 9, 81]))
assert(["abc", "Hi", "AbC"].mapThenUnique { $0.lowercased() } == Set(["hi", "abc"]))
assert(["33", "21", "33"].mapThenUnique { Int($0) } == Set([21, 33]))

var scratch = [Int]()
powers(of: 2, through: 64) { scratch.append($0) }
assert(scratch == [1, 2, 4, 8, 16, 32, 64])
scratch.removeAll()
powers(of: 2, through: 63) { scratch.append($0) }
assert(scratch == [1, 2, 4, 8, 16, 32])
scratch.removeAll()
powers(of: -3, through: 300) { scratch.append($0) }
assert(scratch == [1, -3, 9, -27, 81, -243])

let h: Animal = Horse(name: "CJ")
assert(h.speak() == "CJ says neigh")
let c: Animal = Cow(name: "Bessie")
assert(c.speak() == "Bessie says moooo")
assert(Sheep(name: "Little Lamb").speak() == "Little Lamb says baaaa")

// Test that Animal really is a protocol with the default method
struct Rat: Animal {
    let name: String
    let sound = "squeak"
}
assert(Rat(name:"Oreo").speak() == "Oreo says squeak")

assert(say("A").phrase == "A")
assert(say("A").and("B").phrase == "A B")
assert(say("🐤🦇").and("$🦊👏🏽").and("!").phrase == "🐤🦇 $🦊👏🏽 !")
var greet = say("Hello").and("there")
assert(greet.and("nice").and("person").phrase == "Hello there nice person")
assert(greet.and("Swift").phrase == "Hello there Swift")

assert(twice({$0 * 2}, appliedTo: 5.0) == 20.0)
assert(twice({s in s + "ee"}, appliedTo: "b") == "beeee")

assert(uppercasedFirst(of: [], longerThan: 5) == nil)
assert(uppercasedFirst(of: ["🎃"], longerThan: 1) == nil)
assert(uppercasedFirst(of: ["a", "bcdef"], longerThan: 5) == nil)
assert(uppercasedFirst(of: ["a", "abcdef", "g"], longerThan: 5) == Optional.some("ABCDEF"))
assert(uppercasedFirst(of: ["ab", "abcf"], longerThan: 1) == Optional.some("AB"))

let q = Quaternion(a: 3.5, b: 2.25, c: -100, d: -1.25)
assert(q.a == 3.5)
assert(q.b == 2.25)
assert(q.c == -100.0)
assert(q.d == -1.25)
let q1 = Quaternion(a: 1, b: 3, c: 5, d: 2)
let q2 = Quaternion(a: -2, b: 2, c: 8, d: -1)
let q3 = Quaternion(a: -1, b: 5, c: 13, d: 1)
let q4 = Quaternion(a: -46, b: -25, c: 5, d: 9)
assert(q1 + q2 == q3)
assert(q3 - q2 == q1)
assert(q1 * q2 == q4)
assert(Quaternion.I * Quaternion.J == Quaternion.K)
assert(Quaternion.ZERO.coefficients == [0, 0, 0, 0])
assert(Quaternion.K.coefficients == [0, 0, 0, 1])
assert(Quaternion(a: 2, b: 1.5, c: 10, d: -8).coefficients ==
    [2.0, 1.5, 10.0, -8.0])
assert(String(describing: Quaternion.ZERO) ==
    "0.0+0.0i+0.0j+0.0k")
assert(String(describing: Quaternion(a: 0, b: -1, c:  0, d: 2.25)) ==
    "0.0-1.0i+0.0j+2.25k")
assert(String(describing: Quaternion.ZERO - Quaternion.K) ==
    "0.0+0.0i+0.0j-1.0k")
assert(String(describing: Quaternion(a: -20, b: -1.75, c: 13, d: -2.25)) ==
    "-20.0-1.75i+13.0j-2.25k")

print("All tests passed")

You can test with:

$ swiftc -o main exercises.swift main.swift && ./main

Note: if you cannot install Swift on your machine, say, because you are running Windows, you can develop your code on a site like Replit.

The Module You Are To Write

In the file exercises.swift create a Swift module containing the following entities. For details on how each function or class should work, consult the unit tests.

  1. A function that accepts a number of U.S. cents (an Int) and returns a Result object that can indicate (1) for success, a 4-tuple containing the smallest number of U.S. quarters, dimes, nickels, and pennies that equal the given amount, or (2) a failure whenever a negative amount was supplied. See the unit test for information on how you are to craft the result type. Implementation restriction: use the Int method quotientAndRemainder.
  2. A String computed property that computes a new string equal to the receiver but with all whitespace removed and then with the i-th grapheme (1-based) repeated i times. Note that you are to repeat graphemes, not code units. Good news: Swift does this automatically.
    > "  Hi  hi  ".stretched
    "Hiihhhiiii"
    
    > "😁😂😱".stretched
    "😁😂😂😱😱😱"
    
  3. A method on Array that maps a function over the receiver then returns the unique values after mapping.
  4. A function that generates powers of a given (integer) base, starting at the 0th power (namely, 1) through a given limit, consuming each with a closure. Note that consistent with Swift terminology, “through” here means including the limit value.
    > powers(of: 2, through: 70) { print($0) }
    1
    2
    4
    8
    16
    32
    64
    
  5. An idiomatic Swift solution to the Animal-Cow-Sheep-Horse example that appears in the middle of the course notes on JavaScript. Not that in JavaScript, Python, C++, or Java, you have an Animal superclass with Horse, Cow, and Sheep appearing a subclasses. For your Swift solution, make Animal a protocol, and define the speak function as a default implementation in a protocol extension. Each of the three structs Horse, Cow, and Sheep should adapt the protocol, similar to the way the Rat struct in the supplied unit test script does.
  6. The world-famous “say” function but with a little twist, namely it needs to work like this:
    > say("Hello").phrase
    "Hello"
    
    > say("Hello").and("my").and("name").and("is").and("Colette").phrase
    "Hello my name is Colette"
    
  7. A function that accepts a function f and a value x and returns f(f(x)). Remember you are doing Swift, not Java, so you can use functions directly (no need for objects with apply methods).
  8. A function that returns the upper-cased version of the first string in a list that has a length greater than a given value, wrapped in an Optional, since there might not be any such string in the list. Implementation restriction: Your solution must find the first such value using the first method, then since that returns an optional, use the optional chaining operator ?. when you invoke the upper-casing method. This is so cool, right?
  9. A struct for quaternions, that works similar to what you’ve seen in all previous assignments. Something new for this assignment: make your Quaternions be convertible to strings. Please scour the test script to find all the requirements.