Fun with the RPN calculator in Ioke
In this article, Cédric Beust explains how to port a RPN calculator from Haskell to Fantom. It could have made me want to code more in Haskell or go discover Fantom. Not at all. Reading the article, I felt the urge to code the same algorithm with Ioke. You know, this language I discovered through a MasterMind Kata a while ago.
Here is the Fantom code:
foldingFunction := | Int[] n, Str p -> Int[] | {
echo("n:" + n)
switch(p) {
case "*" : return n.push(n.pop * n.pop)
case "+" : return n.push(n.pop + n.pop)
case "-" : return n.push(n.pop - n.pop)
case "/" : return n.push(n.pop / n.pop)
default: return n.push(p.toInt)
}
};
"8 1 2 + 5 * +".split.reduce([,], foldingFunction)
Here is my first try with Ioke:
rpn=method(sum, x,
case(x,
"+", [sum pop! + sum pop!],
"*", [sum pop! * sum pop!],
"-", [sum pop! - sum pop!],
"/", [sum pop! / sum pop!],
[x toRational]
) + sum
)
"8 1 2 + 5 * +" split fold([], sum, x, rpn(sum, x)) println
Quite neat. The code looks a lot like the Fantom code or even the Haskell code/ Now let’s use the power of Ioke and remove duplication by defining rpn method on List itself :
List rpn = method(x,
append!(case(x,
"+", pop! + pop!,
"*", pop! * pop!,
"-", pop! - pop!,
"/", pop! / pop!,
x toRational
)
)
)
"8 1 2 + 5 * +" split prepend!([]) fold(rpn) println
I like that one better. We could remove some more duplication at the expense of readability, because, you know, there must be another way to convert “+” to , “*” to * and “-” to -:
List rpn = method(x,
append!(case(x,
or("+","*","-","/"), Message fromText(x) appendArgument(pop!) sendTo(pop!),
x toRational
))
)
Here we used reflection, but we could use eval method and regexp:
List rpn = method(x,
append!(if(x !~(#/[-+*/]/),x toRational,Origin eval("#{s pop!}#{x}#{s pop!}))
)
"8 1 2 + 5 * +" split prepend!([]) fold(rpn) println
Ok, now let’s try to recall those days I was an expert in Perl, and compress this to the max:
r=fn(e,e split fold([],s,x,s append!(if(x !~(#/[-+*/]/), x toRational,
Origin eval("#{s pop!}#{x}#{s pop!}")))))
r("8 1 2 + 5 * +") println
This one, I’m not really proud of. Should I?