package Rpn;
use strict;
use warnings;
sub evaluate {
my ($expr) = @_;
my @stack;
for my $tok (split ' ', $expr) {
if ($tok =~ /^-?\d+$/) {
push @stack, $tok;
next;
}
my $x = pop @stack;
defined $x or die "Stack underflow\n";
my $y = pop @stack;
defined $y or die "Stack underflow\n";
if ($tok eq '+') {
push @stack, $y + $x;
} elsif ($tok eq '-') {
push @stack, $y - $x;
} elsif ($tok eq '*') {
push @stack, $y * $x;
} elsif ($tok eq '/') {
push @stack, int($y / $x);
} else {
die "Invalid token:\"$tok\"\n";
}
}
@stack == 1 or die "Invalid stack:[@stack]\n";
return $stack[0];
}
1;
####
use strict;
use warnings;
use Test::More;
use Rpn;
my @normal_tests = (
[ '1 2 +', 3 ],
[ '1 -2 -', 3 ],
[ '-1 2 +', 1 ],
[ '1 2 -', -1 ],
[ '1 2 + 3 -', 0 ],
[ '1 2 - 3 -', -4 ],
[ '1 2 - 5 +', 4 ],
[ '1 2 - 5 + 2 -', 2 ],
[ '1 1 1 1 1 2 + + + + +', 7 ],
[ '1 -5 +', -4 ],
[ '5 3 *', 15 ],
[ '-2 -5 *', 10 ],
[ '2 -5 *', -10 ],
[ '6 4 /', 1 ],
[ '0 1 /', 0 ],
[ '1 0 *', 0 ],
[ '00 1 +', 1 ],
[ '1 00 -', 1 ],
[ '00', 0 ],
[ '-00', 0 ],
[ '1 2 3 * +', 7 ],
[ '3 4 * 2 3 * +', 18 ],
[ '3 4 * 2 / 3 *', 18 ],
[ '3 4 * 5 / 3 *', 6 ],
[ '999999 1000 / 67 * 56 80 * 8 * -', 31093 ],
[ '42', 42 ],
);
my @exception_tests = (
[ '5 4 %', "Invalid token:\"%\"\n" ],
[ '5 +', "Stack underflow\n" ],
[ '+', "Stack underflow\n" ],
[ '5 4 + 42', "Invalid stack:[9 42]\n" ],
[ '', "Invalid stack:[]\n" ],
);
plan tests => @normal_tests + @exception_tests;
for my $t (@normal_tests) {
cmp_ok(Rpn::evaluate($t->[0]), '==', $t->[1]);
}
for my $t (@exception_tests) {
eval { Rpn::evaluate($t->[0]) };
is($@, $t->[1]);
}
##
##
module Rpn-0.0.1-cpan:ASAVIGE;
sub evaluate (Str $expr) returns Int {
my @stack;
for ($expr.split()) -> $tok {
if $tok ~~ rx:Perl5/^-?\d+$/ {
@stack.push($tok);
next;
}
my $x = @stack.pop() err die "Stack underflow\n";
my $y = @stack.pop() err die "Stack underflow\n";
given $tok {
when '+' { @stack.push($y + $x) }
when '-' { @stack.push($y - $x) }
when '*' { @stack.push($y * $x) }
when '/' { @stack.push(int($y / $x)) }
default { die "Invalid token:\"$tok\"\n" }
}
}
@stack.elems == 1 or die "Invalid stack:[@stack[]]\n";
return @stack[0];
}
##
##
#!/usr/bin/pugs
use v6;
use Test;
use Rpn;
my @normal_tests = (
[ '1 2 +', 3 ],
[ '1 -2 -', 3 ],
[ '-1 2 +', 1 ],
[ '1 2 -', -1 ],
[ '1 2 + 3 -', 0 ],
[ '1 2 - 3 -', -4 ],
[ '1 2 - 5 +', 4 ],
[ '1 2 - 5 + 2 -', 2 ],
[ '1 1 1 1 1 2 + + + + +', 7 ],
[ '1 -5 +', -4 ],
[ '5 3 *', 15 ],
[ '-2 -5 *', 10 ],
[ '2 -5 *', -10 ],
[ '6 4 /', 1 ],
[ '0 1 /', 0 ],
[ '1 0 *', 0 ],
[ '00 1 +', 1 ],
[ '1 00 -', 1 ],
[ '00', 0 ],
[ '-00', 0 ],
[ '1 2 3 * +', 7 ],
[ '3 4 * 2 3 * +', 18 ],
[ '3 4 * 2 / 3 *', 18 ],
[ '3 4 * 5 / 3 *', 6 ],
[ '999999 1000 / 67 * 56 80 * 8 * -', 31093 ],
[ '42', 42 ],
);
my @exception_tests = (
[ '5 4 %', "Invalid token:\"%\"\n" ],
[ '5 +', "Stack underflow\n" ],
[ '+', "Stack underflow\n" ],
[ '5 4 + 42', "Invalid stack:[9 42]\n" ],
[ '', "Invalid stack:[]\n" ],
);
plan @normal_tests.elems + @exception_tests.elems;
for @normal_tests -> $t {
cmp_ok(Rpn::evaluate($t[0]), &infix:<==>, $t[1]);
}
for @exception_tests -> $t {
try { Rpn::evaluate($t[0]) };
is($!, $t[1]);
}
##
##
{-# OPTIONS_GHC -fglasgow-exts -Wall #-}
module Rpn (evaluate) where
import Char
isStrDigit :: String -> Bool
isStrDigit = all isDigit
-- Check that a string matches regex /^-?\d+$/.
isSNum :: String -> Bool
isSNum [] = False
isSNum "-" = False
isSNum ('-':xs) = isStrDigit xs
isSNum xs = isStrDigit xs
calc :: Int -> String -> Int -> Int
calc x "+" y = x+y
calc x "-" y = x-y
calc x "*" y = x*y
calc x "/" y = x`div`y
calc _ tok _ = error $ "Invalid token:" ++ show tok
evalStack :: [Int] -> String -> [Int]
evalStack xs y
| isSNum y = (read y):xs
| (a:b:cs) <- xs = (calc b y a):cs
| otherwise = error "Stack underflow"
evaluate :: String -> Int
evaluate expr
| [e] <- el = e
| otherwise = error $ "Invalid stack:" ++ show el
where
el = foldl evalStack [] $ words expr
##
##
{-# OPTIONS_GHC -fglasgow-exts -Wall #-}
-- t1.hs: build with: ghc --make -o t1 t1.hs Rpn.hs
module Main where
import Test.HUnit
import Control.Exception
import Rpn
type NormalExpected = (String, Int)
makeNormalTest :: NormalExpected -> Test
makeNormalTest e = TestCase ( assertEqual "" (snd e) (Rpn.evaluate (fst e)) )
normalTests :: Test
normalTests = TestList ( map makeNormalTest [
( "1 2 +", 3 ),
( "1 -2 -", 3 ),
( "-1 2 +", 1 ),
( "1 2 -", -1 ),
( "1 2 + 3 -", 0 ),
( "1 2 - 3 -", -4 ),
( "1 2 - 5 +", 4 ),
( "1 2 - 5 + 2 -", 2 ),
( "1 1 1 1 1 2 + + + + +", 7 ),
( "1 -5 +", -4 ),
( "5 3 *", 15 ),
( "-2 -5 *", 10 ),
( "2 -5 *", -10 ),
( "6 4 /", 1 ),
( "0 1 /", 0 ),
( "1 0 *", 0 ),
( "00 1 +", 1 ),
( "1 00 -", 1 ),
( "00", 0 ),
( "-00", 0 ),
( "1 2 3 * +", 7 ),
( "3 4 * 2 3 * +", 18 ),
( "3 4 * 2 / 3 *", 18 ),
( "3 4 * 5 / 3 *", 6 ),
( "999999 1000 / 67 * 56 80 * 8 * -", 31093 ),
( "42", 42 )
])
-- Exception wrapper for Rpn.evaluate
-- The idea is to catch calls to the error function and verify
-- that the expected error string was indeed written.
evaluateWrap :: String -> IO String
evaluateWrap x = do res <- tryJust errorCalls
(Control.Exception.evaluate (Rpn.evaluate x))
case res of
Right r -> return (show r)
Left r -> return r
type ExceptionExpected = (String, String)
makeExceptionTest :: ExceptionExpected -> Test
makeExceptionTest e = TestCase ( do x <- evaluateWrap (fst e)
assertEqual "" (snd e) x )
exceptionTests :: Test
exceptionTests = TestList ( map makeExceptionTest [
( "5 4 %", "Invalid token:\"%\"" ),
( "5 +", "Stack underflow" ),
( "+", "Stack underflow" ),
( "5 4 + 42", "Invalid stack:[42,9]" ),
( "", "Invalid stack:[]" )
])
main :: IO Counts
main = do runTestTT normalTests
runTestTT exceptionTests
##
##
(Control.Exception.evaluate (Rpn.evaluate x))
##
##
f (x:y:zs) "+" = y+x:zs
##
##
import Debug.Trace
-- ...
f (x:y:zs) "+" = trace ("+" ++ show x ++ ":" ++ show y ++ ":" ++ show zs) (y+x:zs)
##
##
autrijus stares at type Eval x = forall r. ContT r (ReaderT x IO) (ReaderT x IO x)
and feels very lost
Didn't you write that code?
yeah. and it works
I just don't know what it means.