Re: [SPOILER] Ruby RPNCalc (Solution to Quiz #25)
Mike Stok <[email protected]> Fri, 1 Oct 2004 22:01:18 -0400 (EDT)
| Newsgroups | gmane.comp.lang.perl.qotw.discuss |
|---|---|
| Message-ID | <[email protected]> |
On Fri, 1 Oct 2004, James Edward Gray II wrote:
> I better start with a big thanks to the quiz writer, because I thought
> this one was a blast. Especially for those of us that have found
> memories of our HP calculators.
>
> Below is my solution in Ruby. I tried to be pretty liberal with my
> comments, to make it easier to follow, even if you aren't a Ruby
> person. Unfortunately, I think it requires Ruby 1.8.x, which many
> people would probably need to install. Sorry about that.
>
> I believe my solution covers everything in the quiz and then some, but
> I better qualify a few points...
Here's my quick ruby RP calculator program & module, the interface to the
calculator object is simple, you enter one or more numbers, and do one or
more operations. In action:
[mike@ratdog 025]$ ./rpcalc
> 10101 0x10101 0b10101 010101
0:[4161]
1:[21]
2:[65793]
3:[10101]
> **
0:[568179274422623438952525153785800548536498157874977327044688898089602930883890155950164274210872183445236961783406805550438322777586001712779851306998072988080327268714418874307383988352213907058951896803013978203519590845139405127235060300306738246373902109123881982857353181559005823461815348572678275728637884312298022134693068498166756871918006257214730951033702927783123911852184715207346238474341440592866380170782308154490427309487955042797263178532440550307862212637768141671021284306484191330642412551228550609294204074872915756188435946841350784829229055114581876928779818855515251555394693392643133209189672896721433274665114251503684774689535091670902536628774741888397780688615228976094478226394100362753820633761255621434983907452655178775614247608444886720894414297893788773308559959336705618127984122939908545286085788249368593985476894879640880185014464776513011568864827263572191661939108637649583001451947975447305167324438999670512821496747174926546399507073297040563203204052372400196881043581583781064972871334363018791272081820382549037235300485872591635812121415227073315421558826409074429599356214961891128758769729647264253269216235491698339052937369606724869297677022397314418651333259408126967422208346703502793656838055452783170760043450692562609690086418039516345370631745378358175393695276091158652464472686970120622601519823053803177778029682393263327023057803959919540237010126075944167779077227191865628897354644423099739841059686839076931660063939142429721592338994057999265701506725661520875743233201165814582855122256545095323382485055233815850045399645161441148833136077212809392371551211521080573031128409934665674142718422272556899651681072576266937169718492503842661034590948092405588394074810000573225076384207458347235457468566920881820391232998636242795759743403295558686065055833532305868463396024127379095401378574881975183512057475108626776792759866845765589478198657644496364553759508568046754559232564594217792240280986105475950168843949966168259547742721616976329172163850275827773157334901441432657008208849906023018619420168044616132792365746633034264018365876108551152934131165096827697347745164818558516407891470138896664312723313018485999661686210419343961177034048747755977895452658012415304155789177332944132895674395956506002095342789983592438181614103056172402235524845026734381177326146506665566714945330764551628928384556476950955409022129463082379862997370895016997157240533895436194502250871577037991299773138121532388322708037065728002477667857546701602753030563225452202187664067667963701137544552665900719814823225351412073748056787321007825567469760031802333137323788803434709231571912743158048093703598752262906312431955233860960890385244097014892289827685806631759914661314375069386124662763599786005860725780476750512545673230326772220189663248693721352655382712925808940537338124059414049570286221227016213966724317027803321529475066153708984838536384197350576914206923160668876517632142292888410505270208608259374245507070041627458591937847323636674655757690750081561091253093633448696019006376847395746529995154016151906812149216457221991370896717872383012149498834943980848253789148057849710090359039734067413345671635433438226123655414842341594761635909545519736748325528984816208426242742499016816463876523477513880965698320195748470106818750215672263802134255846023971413353691395586042662356431580180732082519930343312629875756416433013939413395859269045757903344046817731720315504839763135393246759681649508844350999359074000451136406848345578370665703512013045362509576892110509314705049776264600647256793298660700205219429361925321342571818970700146761577727240923087270542079434164704837923909304072427652370350044159764193989546758615694096411598838589416773223885439766376919600100939781977933979401260541768696134049099352165240014019676391068804950019220776034474117343644182784237200871562164325491516934523415203117808871426259237302546042217908622961564365389722112253235116090335607767811563109215996355078914812968752812757655035218739605764030642949691830915125880615752952739466587665928229317042461692787513730252745354203321459333802629441191306512586432268127393524292531799762498207028236871785307489534274949880190721649585085754634917713849697338350938779124333743209752786388553938724260267946185911464730745996418457422069882037602626750158417928510882586480011171642550621474603437267111027413492635044279760976709537714968834114645373422483224466032037093824473627400939850006835623051800864937952313570684635901603596219743231753783877901362475328509754772461392576971334722740416176061344837835713489347310199712303156487440385912291064878196761019756050373043483740281333909211725389554364026863492229115526791769819849817766006075589010683833892513329900461770361520184282655508106694105593621947851764631524638743891736307982980091624134983713446040046100822580399359059439832140564549753691512071471468416610338573714873903166214809853534435395651147130936657775052668422393110669837893075562919781732196219537030488241754877915214275405657841825467332459709367948472889379146989023988189535312246660322106036199268032127833115783281499995488928858410404673206086750047970092107362404218033342377520122156533671628415968139821491370124772320025440648682640316674668347741442110232770626591816620509829940396776760775531567470137897028395469025755212621063132863157439483220539083774623976953526973769514551907600573130892962853779485579450757632652238230299574518736721116001116082473758355221]
1:[65793]
2:[10101]
> drop swap hex
0:[2775]
1:[10101]
etc.
I think it needs ruby 1.8.2 (recent snapshot) but I am sleep deprived at
the moment so could be wrong! A slightly different approach, I'm just
re-igniting my Ruby spark, so there is much which could be improved. Hope
you like it.
Thanks to the guys who take the time to write the quizzes & summarise.
Mike
Program:
#!/usr/bin/env ruby
require 'rpcalc'
calc = RpCalc.new
loop do
print '> '
break unless line = gets
line.split(' ').each do |item|
if num = (Integer(item) rescue Float(item) rescue nil)
calc.enter(num)
else
begin
calc.do(item)
rescue
puts "oops"
break # ditch rest of line
end
end
end
puts calc
end
and the module (+ unit tests :-)
class RpCalc
# class implementing a reverse polish calculator for perl
# quiz of the week 25
# quick & easy way to deal with the 2 / 3 = 0 problem :-)
require 'mathn'
# @@procs contains procs for anything whose effect on the
# stack is predictable in terms of number of arguments required
# and number of results returned.
#
# "Irregular" operations are handled in do()
@@procs = Hash.new
# binary ops which Ruby can handle for me
%w{ + - * / % ** }.each do |op|
@@procs[op] = proc { |a, b| b.send(op, a) }
end
# other operations
@@procs['swap'] = proc { |a, b| [b, a] }
@@procs['drop'] = proc { |a| [] }
# n.b. can't dup Fixnums, hence rescue...
@@procs['dup'] = proc { |a| [a, (a.dup rescue a)] }
# hmmm, why do I need the 0 + here... ruby 1.8.2 snapshot
# may be to blame, and there must be a sexier way to do this.
@@procs['sin'] = proc { |a| 0 + Math.sin(a) }
@@procs['neg'] = proc { |a| -a }
def initialize
do_clear
do_dec
end
# Enter value(s) onto the stack
def enter(*values)
values.each do |value|
@stack.unshift(value)
end
end
# Return contents of the stack as "normal" ruby types
#
# This hides mathn Rational results from the outside world.
# Don't know if it's necessary or not.
def to_a
@stack.collect { |n| n.integer? ? n.to_i : n.to_f }
end
def to_s
print = []
self.to_a.each_with_index do |val, i|
print << "#{i}:[#{val.integer? ? val.to_s(@base) : val}]"
end
print.join("\n")
end
# Perform an operation
def do(*ops)
ops.each do |op|
# Checks for the operation in @@procs, if operation not found there
# then see if we have a do_<operation> we can use before failing
if p = @@procs[op]
n_args = p.arity
raise RuntimeError, "stack empty" if @stack.size < n_args
args = @stack.shift(n_args)
@stack.unshift(*p[*args])
else
self.send("do_#{op}") rescue raise RuntimeError, "bad command"
end
end
end
protected
# clear the stack
def do_clear; @stack = []; end
# base setters
def do_bin; @base = 2; end
def do_oct; @base = 8; end
def do_dec; @base = 10; end
def do_hex; @base = 16; end
end
if __FILE__ == $0
require 'test/unit'
class TC_Spec < Test::Unit::TestCase
# test the things described in the problem description
def test_simple
r = RpCalc.new
assert_equal([], r.to_a, 'initially empty')
r.enter(3)
assert_equal([3], r.to_a, 'enter 3')
r.enter(5)
assert_equal([5, 3], r.to_a, 'enter 5')
r.do('+')
assert_equal([8], r.to_a, 'add')
r.enter(2)
r.do('*')
assert_equal([16], r.to_a, 'multiply')
r.enter(2, 3)
r.do('+', '/')
assert_equal([3.2], r.to_a, 'add and divide')
assert_raises(RuntimeError, 'too few stack entries') { r.do('/') }
end
# test extra operators
def test_ops
r = RpCalc.new
r.enter(4, 2)
r.do('-')
assert_equal([2], r.to_a, '-')
r.do('clear')
r.enter(123, 45)
r.do('%')
assert_equal([33], r.to_a, '%')
r.do('clear')
r.enter(2, 16)
r.do('**')
assert_equal([65536], r.to_a, '**')
end
# test commands
def test_commands
r = RpCalc.new
r.do('clear')
assert_equal([], r.to_a, 'clear empty stack')
r.enter(1, 2, 3)
r.do('clear')
assert_equal([], r.to_a, 'clear stack with contents')
r.enter(1, 2, 3)
assert_equal([3, 2, 1], r.to_a, 'enter order (to remind me)')
r.do('swap')
assert_equal([2, 3, 1], r.to_a, 'swap')
r.do('clear')
r.enter(1)
assert_raises(RuntimeError, 'swap too few values') { r.do('swap') }
r.do('clear')
r.enter(1, 2, 3)
r.do('drop')
assert_equal([2, 1], r.to_a, 'drop')
r.do('clear')
assert_raises(RuntimeError, 'drop empty stack') { r.do('drop') }
r.do('clear')
assert_raises(RuntimeError, 'dup empty stack') { r.do('dup') }
r.enter(1, 2, 3)
r.do('dup')
assert_equal([3, 3, 2, 1], r.to_a, 'dup')
assert_raises(RuntimeError, 'bad command test') { r.do(nil) }
end
# try adding sin
def test_misc
r = RpCalc.new
r.enter(3)
r.do('sin')
assert_equal([Math.sin(3)], r.to_a, 'sin')
r.do('neg')
assert_equal([-Math.sin(3)], r.to_a, 'neg')
end
# utility
def test_utility
r = RpCalc.new
assert_equal('', r.to_s, 'empty to string')
r.enter(9)
assert_equal('0:[9]', r.to_s, 'one entry to string')
r.do('bin')
assert_equal('0:[1001]', r.to_s, 'one entry to binary string')
r.do('oct')
assert_equal('0:[11]', r.to_s, 'one entry to octal string')
r.do('hex')
r.enter(17)
assert_equal("0:[11]\n1:[9]", r.to_s, 'two entries to hex string')
end
end
end
--
[email protected] | The "`Stok' disclaimers" apply.
http://www.stok.co.uk/~mike/ | GPG PGP Key 1024D/059913DA
| Fingerprint 0570 71CD 6790 7C28 3D60
| 75D2 9EC4 C1C0 0599 13DA