PLACE |
spec |
solutions |
D |
The following program is written in D2, the current (as of 2010) version of D and of its standard library Phobos.
The outer foreach
loop reads lines from the standard input and assigns each one in turn to a string s
. An internal foreach
loop breaks s
down (split
) into tokens, and iterates over the resulting sequence by processing each token tk
in it.
A dynamic array nums
is being made use of for maintaining a stack of intermediate values in the course of evaluating an RPN expression. If a token happens to represent an arithmetic operator, that operator is applied to a pair of numbers pulled off the stack. Otherwise tk
must parse as a number, and that number is obtained using the library function to
. In both cases the result x
gets pushed onto the stack.
Once all tokens are processed this way, the result x
of evaluating the RPN expression is printed out. If the token sequence produced by split
was actually empty, the inner foreach
loop does nothing and consequently nums
remains empty, so nothing is printed in this case.
nums
is initially empty and gets cleared after processing each input line by setting its length
to 0. Augmenting and reducing the stack is done by using the concatenation operator ~=
and the function pop
, respectively. Referencing and removing the last element of nums
in pop
makes use of standard library function calls.
The token processing and its epilogue are nested into a try–catch
block so that possible syntax errors in the RPN expression can be caught. Such an error would cause an exception to be raised and eventually handled by printing out an error message. Clearing the stack is put in a finally
clause in order to ensure that it takes place regardless of whether an exception occured or not.
An exception occurs in three different ways. One is within pop
, trying to reference an item in an empty nums
. Another is within to
, when a token tk
does not read as a number. Exception is also being thrown explicitly when there is more than a single number left on the stack after the whole RPN expression is processed. The argument of throw
is not used and therefore arbitrary, provided that it is an instance of the Exception
class.
Both loops in the program are abstract, in the sense that they are implicitly controlled by the sequences (ranges, in D's parlance) which they traverse. Thus, the inner loop does as many iterations as there are tokens, and the outer terminates as soon as there are no more lines to read.
As can be observed in this program, D is capable of inferring the types of program objects that depend on other program objects. For example, both ‘loop variables’ s
and tk
are strings, but this is deduced from the types of byLine
and split
rather than stated in the text. The type of t
within pop
, as well as the return type of pop
itself, are also omitted (by writing the formal substitute auto
instead) and automatically inferred from the element type of nums
.
Apart from nums
, the type double
is given in two other places. In those places, rather than double
, we could have written, e.g., typeof(nums[0])
, meaning ‘the element type of nums
’. That would probably be overkill for this particular program, but relative type specification is a useful device in general.
The function to
is overloaded and templatized for many kinds of conversions between data types. The type of its argument, string (tk
) in this case, selects among all instantiations of to
the family that converts from string values. To pick up the specific instance within this family that produces double
s, to
is also given a template argument: a type specification that precedes the value argument and is delimited from the function's name by !
.
All functions and other items that belong to D's standard library reside in modules, so accessing such functions requires import
ing the modules. Thus, the std.stdio
module is imported so that stdin
and writeln
can be used, the std.array
module is needed for accessing empty
, length
, split
, back
, and popBack
, and std.conv
is imported to enable the use of to
.
import std.stdio, std.array, std.conv; double[] nums; auto pop() {auto t = nums.back; nums.popBack; return t;} void main() { foreach (s; stdin.byLine()) try { double x; foreach (tk; s.split()) { switch (tk) { case "+": x = pop()+pop(); break; case "*": x = pop()*pop(); break; case "-": x = pop(); x = pop()-x; break; case "/": x = pop(); x = pop()/x; break; default: x = to!double(tk); } nums ~= x; } if (nums.length>1) throw new Exception(""); if (!nums.empty) writeln(x); } catch(Throwable) {writeln("error");} finally nums.length = 0; }
boykobbatgmaildotcom