CS470/570 Problem Set 1 (PS1): Solutions

Revision History
None so far

For the details of each problem was graded, here's the so-called rubric.

1 Most of the class has excellent tasteTaste in movies: Pan's Labyrinth, A Clockwork Orange, Saving Private Ryan, In Bruges, and others. My own favorite is Twelve Monkeys, a beautifully conceived and executed time-travel movie; and I hate time-travel movies.

2A Here's one way to do it.

object MyWeight {
  val MIN_WEIGHT: Double = 40
  val MAX_WEIGHT: Double = 75

  private var weight: Double = 55

  def gain(w: Double): Unit = {
    val okayBefore: Boolean = weight >= MIN_WEIGHT && weight <= MAX_WEIGHT
    weight += w
    val okayAfter: Boolean =  weight >= MIN_WEIGHT && weight <= MAX_WEIGHT
    if (okayBefore && !okayAfter) {
      if (weight < MIN_WEIGHT)
        throw new Exception("Weight below minimum")
      else
        throw new Exception("Maximum weight exceeded")
    }
  }

  def getWeight: Double = weight
}

object MyWeightApp extends App {

  // String that represents a Double to 1 decimal digit of accuracy –
  def doubleApprox(d: Double): String = "%5.1f".format(d).trim

  // String that represents MyWeight's weight member to within
  // one decimal digit of accuracy –
  def getWeightApprox: String = doubleApprox(MyWeight.getWeight)

  val HOLIDAY_INCREASE: Double = 5
  val NEW_YEARS_DECREASE: Double = 4
  val MAX_YEARS = 50
  println("Start " + getWeightApprox)
  var i = 1
  try {
    while (i < MAX_YEARS) {
      print("Year " + i)
      print(" gain " + doubleApprox(HOLIDAY_INCREASE))
      MyWeight.gain(HOLIDAY_INCREASE)
      println(" lose " + doubleApprox(NEW_YEARS_DECREASE))
      MyWeight.gain(-NEW_YEARS_DECREASE)
      println("At year end: " + getWeightApprox)
      i += 1
    }
  } catch {
      case ex: Exception => {
        println("\n" + ex.getMessage + "; weight = " + getWeightApprox)
      }
  }
  if (i >= MAX_YEARS)
    println("Maximum time horizon exceeded; weight = " + getWeightApprox)
}

2B There are several possible ways of amending the spec. However, a spec is not the same as an implementation, and a revised spec is not a recipe for changing the existing program. So spelling out details of which parts of the program have to change and how is not allowed. That said, my original spec already crossed the line into implementation details, when I gave the name of the private var used to store the subject's current weight. If it's private, then the name can't be used outside the classprivate where it's defined and a spec has no business picking a name for it or even how it's represented. All users of MyWeight should know is that the current weight (original weight + all increments) can be retrieve using getWeight and changed using gain. I violated this rule to make the problem easier to grade. Mea culpa. So I only took 1 point off if you violated it even more egregiously.

Here is a nice way to reviseRevise the spec so no exception is thrown as long the weight is moving toward the legal zone:Constant names

MyWeight should be mutable, but have three constant public members:

  1. MAX_WEIGHT: The maximum desirable weight in kilograms (a Double).
  2. MIN_WEIGHT: The minimum desirable weight in kilograms (a Double). This must be less than MAX_WEIGHT.
  3. INITIAL_WEIGHT: The initial weight of the entity being weighed (EBW). We require that MIN_WEIGHT ≤ MAX_WEIGHT but nothing prevents INITIAL_WEIGHT from being outside this range.

The object should have two public methods with this behavior:

  1. MyWeight.gain(w): Records a weight gain of w (a Double). The argument w may be negative. The "header" of gain is
       def gain(w: Double): Unit
    
  2. MyWeight.getWeight: Returns the current net weight of the EBW, defined as the INITIAL_WEIGHT plus all the changes recorded using gain. The "header" of getWeight is
    def getWeight: Double
    

If the net weight ever falls below L or rises above H, gain should throw an exception. (You may create one or two new classes of exception, but you don't have to. Just evaluate throw new Exception(string describing what happened).) L is defined as the smaller of the EBW's initial weight and MIN_WEIGHT; H is defined as the larger of the EBW's initial weight and MAX_WEIGHT. (This idea is due to one of the students. I don't remember which one, but if it's you and you want to be acknowledged by name let me know.) The point is to allow the EBW to fluctuate outside of the interval [MIN_WEIGHT, MAX_WEIGHT] so long as they never go above their highest recorded weight or below their lowest.

MyWeightApp should have the object MyWeight fluctuate up and down in net weight, gaining 5 kg and losing 4 kg every year. To make the code easier to revise, these constants should be written in only one place. It should print out the lines:

Start: 65.0 kg
Year 1: gain 5.0 lose 4.0
At year end: 66.0 kg
Year 2: gain 5.0 lose 4.0
At year end: 67.0 kg
Year 3: ...

until an exception is thrown. Your program should catch it and print:

Maximum weight exceeded; weight = ...

The program should iterate for at most 50 years; after that you can eat whatever you want. If this happens without exceeding the maximum weight, the program prints

Maximum time horizon exceeded; weight = ...

So no matter what it always prints the final value of weight.


Notes

Note Taste
Meaning I like many of the same movies.[Back]

Note private
Exactly what private means in Scala is somewhat under user control, but the default is roughly as in Java: A private variable can only be accessed in a class. Normally that means other objects of the same class as MyWeight can access it. Because MyWeight is defined with object, there are no other objects of the same class. [Back]

Note Revise
I have made changes here and there for other purposes than improving the object's behavior. The main other purpose is to clean up infelicities in the original.[Back]

Note Constant names
I should have read the Scala Style Guide more carefully: the names of constants are capitalized, but the ALL_CAPS_WITH_UNDERSCORES convention used in Java is not. I will avoid it from now on, but for consistency will continue its use in my amended spec. [Back]