Friday, November 9, 2012

Think Functionally - Writing program the functional way

After taking the "Functional Programming Principles in Scala" course on Coursera.org, my understanding and appreciation for "thinking" functionally grew a lot. One of the big benefits the course have on me is fixing my misconception on recursion. I had disliked writing recursive functions thinking it was very susceptible to stack overflow. Although this is true, the course explanation on tail call help correct my misunderstanding.

I was just browsing the IT forum on discuss.com.hk and came across a post on a university question on about writing a program to give change to for a vending machine (note it is in Chinese). Don't worry about he forum. Although there are a definitely a few good developers in there, the questions and discussion are generally of pretty low quality. I just browse occasionally hoping it will help me getting to know the IT industry in Hong Kong a little better.

I recall doing a similar problem in university for either an assignment or exam question, and I recall doing it the "Imperative way" with loops and states. It was an awful experience.
Now arm with thinking functionally, I took another crack at it and seems much easier.

object CoinMachine {
def changeWithBigCoin(accum: Change, changeOwing: Double, avaliableChange: Change): (Change, Double, Change) = {
if (changeOwing < 0) throw new Error("Cannot give change with existing coins")
else if (changeOwing == 0) return (accum, 0, avaliableChange)
else if (changeOwing >= 5 && avaliableChange.fiveDollarsLeft > 0) changeWithBigCoin(accum.changeFiveDollar(1), changeOwing - 5, avaliableChange.changeFiveDollar(-1))
else if (changeOwing >= 2 && avaliableChange.twoDollarsLeft > 0) changeWithBigCoin(accum.changeTwoDollar(1), changeOwing - 2, avaliableChange.changeTwoDollar(-1))
else if (changeOwing >= 1 && avaliableChange.dollarsLeft > 0) changeWithBigCoin(accum.changeDollar(1), changeOwing - 1, avaliableChange.changeDollar(-1))
else if (changeOwing >= 0.5 && avaliableChange.fiftycentsLeft > 0) changeWithBigCoin(accum.changeFiftyCent(1), changeOwing - 0.5, avaliableChange.changeFiftyCent(-1))
else throw new Error("Cannot give change on remainder: " + changeOwing)
}
def pay(paidAmount: Double, cost: Double, availableChange: Change): Change = {
val requiredChange: Double = paidAmount - cost
if (requiredChange < 0) throw new Error("Pay me more")
else if (requiredChange == 0) return new Change(0, 0, 0, 0)
else {
val result = changeWithBigCoin(new Change(0, 0, 0, 0), requiredChange, availableChange)
result._1
}
}
pay(10, 7, new Change(2, 2, 0, 5))
class Change(fiftycents: Int, dollars: Int, twoDollars: Int, fiveDollars: Int) {
def changeFiveDollar(amt: Int) = new Change(fiftycents, dollars, twoDollars, fiveDollars + amt)
def changeTwoDollar(amt: Int) = new Change(fiftycents, dollars, twoDollars + amt, fiveDollars)
def changeDollar(amt: Int) = new Change(fiftycents, dollars + amt, twoDollars, fiveDollars)
def changeFiftyCent(amt: Int) = new Change(fiftycents + amt, dollars, twoDollars, fiveDollars)
override def toString() = "(" + fiftycents + ", " + dollars + ", " + twoDollars + ", " + fiveDollars + ")"
def fiftycentsLeft = fiftycents;
def dollarsLeft = dollars;
def twoDollarsLeft = twoDollars;
def fiveDollarsLeft = fiveDollars
}
}

No comments:

Post a Comment