File Coverage

blib/lib/Math/Calculator.pm
Criterion Covered Total %
statement 51 51 100.0
branch 12 12 100.0
condition 3 3 100.0
subroutine 35 35 100.0
pod 22 22 100.0
total 123 123 100.0


line stmt bran cond sub pod time code
1 2     2   1462 use strict;
  2         5  
  2         60  
2 2     2   22 use warnings;
  2         4  
  2         2060  
3             package Math::Calculator 1.023;
4             # ABSTRACT: a multi-stack calculator class
5              
6             #pod =head1 SYNOPSIS
7             #pod
8             #pod use Math::Calculator;
9             #pod
10             #pod my $calc = Math::Calculator->new;
11             #pod
12             #pod $calc->push(10, 20, 30);
13             #pod $calc->add;
14             #pod $calc->root; # 1.0471285480509 (50th root of 10)
15             #pod
16             #pod =head1 DESCRIPTION
17             #pod
18             #pod Math::Calculator is a simple class representing a stack-based calculator. It
19             #pod can have an arbitrary number of stacks.
20             #pod
21             #pod =method new
22             #pod
23             #pod This class method returns a new Math::Calculator object with one stack
24             #pod ("default").
25             #pod
26             #pod =cut
27              
28             sub new {
29 2     2 1 1022 bless {
30             stacks => { default => [] },
31             current_stack => 'default'
32             } => shift
33             }
34              
35             #pod =method current_stack
36             #pod
37             #pod $calc->current_stack($stackname)
38             #pod
39             #pod This method sets the current stack to the named stack. If no stack by the
40             #pod given name exists, one is created and begins life empty. Stack names are
41             #pod strings of word characters. If no stack name is given, or if the name is
42             #pod invalid, the stack selection is not changed.
43             #pod
44             #pod The name of the selected stack is returned.
45             #pod
46             #pod =cut
47              
48             sub current_stack {
49 68     68 1 123 my ($self, $stack) = @_;
50              
51 68 100 100     390 return $self->{current_stack} unless $stack and $stack =~ /^\w+$/;
52 2 100       8 $self->{stacks}{$stack} = [] unless defined $self->{stacks}{$stack};
53 2         8 $self->{current_stack} = $stack;
54             }
55              
56             #pod =method stack
57             #pod
58             #pod $calc->stack($stackname)
59             #pod
60             #pod This method returns a (array) reference to the stack named, or the current
61             #pod selected stack, if none is named.
62             #pod
63             #pod =cut
64              
65 76 100   76 1 266 sub stack { $_[0]->{stacks}->{$_[1] ? $_[1] : $_[0]->current_stack} }
66              
67             #pod =method top
68             #pod
69             #pod $calc->top
70             #pod
71             #pod This method returns the value of the top element on the current stack without
72             #pod altering the stack's contents.
73             #pod
74             #pod =cut
75              
76 5     5 1 12 sub top { (shift)->stack->[-1] }
77              
78             #pod =method clear
79             #pod
80             #pod $calc->clear
81             #pod
82             #pod This clears the current stack, setting it to C<()>.
83             #pod
84             #pod =cut
85              
86 6     6 1 12 sub clear { @{(shift)->stack} = (); }
  6         13  
87              
88             #pod =method push
89             #pod
90             #pod $calc->push(@elements);
91             #pod
92             #pod C pushes the given elements onto the stack in the order given.
93             #pod
94             #pod =method push_to
95             #pod
96             #pod $calc->push_to($stackname, @elements)
97             #pod
98             #pod C is identical to C, but pushes onto the named stack.
99             #pod
100             #pod =cut
101              
102             sub push { ## no critic
103 36     36 1 64 push @{(shift)->stack}, @_;
  36         65  
104             }
105 3     3 1 6 sub push_to { CORE::push @{(shift)->stack(shift)}, @_; }
  3         6  
106              
107             #pod =method pop
108             #pod
109             #pod $calc->pop($howmany)
110             #pod
111             #pod This method pops C<$howmany> elements off the current stack, or one element, if
112             #pod C<$howmany> is not defined.
113             #pod
114             #pod =method pop_from
115             #pod
116             #pod $calc->pop_from($stack, $howmany);
117             #pod
118             #pod C is identical to C, but pops from the named stack. C<$howmany>
119             #pod defaults to 1.
120             #pod
121             #pod =cut
122              
123             sub pop { ## no critic
124 17 100   17 1 28 splice @{$_[0]->stack}, - (defined $_[1] ? $_[1] : 1);
  17         28  
125             }
126 3 100   3 1 5 sub pop_from { splice @{$_[0]->stack($_[1])}, - (defined $_[2] ? $_[2] : 1); }
  3         7  
127              
128             #pod =method from_to
129             #pod
130             #pod $calc->from_to($from_stack, $to_stack, [ $howmany ])
131             #pod
132             #pod This pops a value from one stack and pushes it to another.
133             #pod
134             #pod =cut
135              
136 2     2 1 7 sub from_to { $_[0]->push_to($_[2], $_[0]->pop_from($_[1], $_[3])) }
137              
138             #pod =method dupe
139             #pod
140             #pod $calc->dupe;
141             #pod
142             #pod C duplicates the top value on the current stack. It's identical to C<<
143             #pod $calc->push($calc->top) >>.
144             #pod
145             #pod =cut
146              
147 2     2 1 7 sub dupe { $_[0]->push( $_[0]->top ); }
148              
149             #pod =method _op_two
150             #pod
151             #pod $calc->_op_two($coderef)
152             #pod
153             #pod This method, which is only semi-private because it may be slightly refactored
154             #pod or renamed in the future (possibly to operate on I elements), pops two
155             #pod elements, feeds them as parameters to the given coderef, and pushes the result.
156             #pod
157             #pod =cut
158              
159             # sub _op_two { $_[0]->push( $_[1]->( $_[0]->pop(2) ) ); $_[0]->top; }
160              
161 14     14   46 sub _op_two { ($_[0]->_op_n(2, $_[1]))[-1] }
162             sub _op_n {
163 16     16   37 $_[0]->push(my @r = $_[2]->( $_[0]->pop($_[1]) ));
164 16 100       97 wantarray ? @r : $r[-1]
165             }
166              
167             #pod =method twiddle
168             #pod
169             #pod This reverses the position of the top two elements on the current stack.
170             #pod
171             #pod =cut
172              
173 2     2 1 8 sub twiddle { (shift)->_op_two( sub { $_[1], $_[0] } ); }
  2     2   11  
174              
175             #pod =method add
176             #pod
177             #pod x = pop; y = pop;
178             #pod push x + y;
179             #pod
180             #pod This pops the top two values from the current stack, adds them, and pushes the
181             #pod result.
182             #pod
183             #pod =method subtract
184             #pod
185             #pod x = pop; y = pop;
186             #pod push x - y;
187             #pod
188             #pod This pops the top two values from the current stack, subtracts the second from
189             #pod the first, and pushes the result.
190             #pod
191             #pod =method multiply
192             #pod
193             #pod x = pop; y = pop;
194             #pod push x * y;
195             #pod
196             #pod This pops the top two values from the current stack, multiplies them, and
197             #pod pushes the result.
198             #pod
199             #pod =method divide
200             #pod
201             #pod x = pop; y = pop;
202             #pod push x / y;
203             #pod
204             #pod This pops the top two values from the current stack, divides the first by the
205             #pod second, and pushes the result.
206             #pod
207             #pod =cut
208              
209 4     4 1 17 sub add { (shift)->_op_two( sub { (shift) + (shift) } ); }
  4     4   20  
210 1     1 1 4 sub subtract { (shift)->_op_two( sub { (shift) - (shift) } ); }
  1     1   10  
211 1     1 1 6 sub multiply { (shift)->_op_two( sub { (shift) * (shift) } ); }
  1     1   5  
212 2     2 1 7 sub divide { (shift)->_op_two( sub { (shift) / (shift) } ); }
  2     2   12  
213              
214             #pod =method modulo
215             #pod
216             #pod x = pop; y = pop;
217             #pod push x % y;
218             #pod
219             #pod This pops the top two values from the current stack, computes the first modulo
220             #pod the second, and pushes the result.
221             #pod
222             #pod =method raise_to
223             #pod
224             #pod x = pop; y = pop;
225             #pod push x ** y;
226             #pod
227             #pod This pops the top two values from the current stack, raises the first to the
228             #pod power of the second, and pushes the result.
229             #pod
230             #pod =method root
231             #pod
232             #pod x = pop; y = pop;
233             #pod push x ** (1/y);
234             #pod
235             #pod This pops the top two values from the current stack, finds the Ith root of
236             #pod I, and pushes the result.
237             #pod
238             #pod =method sqrt
239             #pod
240             #pod This method pops the top value from the current stack and pushes its square
241             #pod root.
242             #pod
243             #pod =cut
244              
245             ## no critic Subroutines::ProhibitBuiltinHomonyms
246 1     1 1 6 sub modulo { (shift)->_op_two( sub { (shift) % (shift) } ); }
  1     1   6  
247 1     1 1 2 sub sqrt { my ($self) = @_; $self->push(2); $self->root; }
  1         3  
  1         3  
248             ## use critic
249 1     1 1 4 sub raise_to { (shift)->_op_two( sub { (shift) **(shift) } ); }
  1     1   7  
250 2     2 1 16 sub root { (shift)->_op_two( sub { (shift)**(1/(shift)) } ); }
  2     2   10  
251              
252             #pod =method quorem
253             #pod
254             #pod =method divmod
255             #pod
256             #pod This method pops two values from the stack and divides them. It pushes the
257             #pod integer part of the quotient, and then the remainder.
258             #pod
259             #pod =cut
260              
261 2     2   3 sub _quorem { my ($n,$m) = @_; (int($n/$m), $n % $m) }
  2         17  
262 1     1 1 4 sub quorem { (shift)->_op_n(2, \&_quorem ); }
263 1     1 1 3 sub divmod { (shift)->_op_n(2, \&_quorem ); }
264              
265             #pod =head1 TODO
266             #pod
267             #pod I'd like to write some user interfaces to this module, probably by porting
268             #pod Math::RPN, writing a dc-alike, and possibly a simple Curses::UI interface.
269             #pod
270             #pod I want to add BigInt and BigFloat support for better precision.
271             #pod
272             #pod I'd like to make Math::Calculator pluggable, so that extra operations can be
273             #pod added easily.
274             #pod
275             #pod =head1 SEE ALSO
276             #pod
277             #pod =for :list
278             #pod * L
279             #pod * L
280             #pod
281             #pod =head1 THANKS
282             #pod
283             #pod Thanks, also, to Duan TOH. I spent a few days giving him a crash course in
284             #pod intermediate Perl and became interested in writing this class when I used it as
285             #pod a simple example of how objects in Perl work.
286             #pod
287             #pod =cut
288              
289             1;
290              
291             __END__