4
\$\begingroup\$

I have written a change calculator using Ada. when the user inputs an amount of money it returns a list of the coins and bills needed to create that total. It starts off by trying to use the largest money types then tries smaller ones.

I am still learning how to use Ada and would like to know how i can make better use of the language features. Am i using the right data types, is there a way to make the change calculation loop more readable?

with Ada.Text_IO;
with Ada.Float_Text_IO;
with Ada.IO_Exceptions;
with Ada.Integer_Text_IO;
use Ada;

procedure Change_Calculator is
   type Money is delta 0.01 digits 10;
   Input_Amount : Money := 0.0;
   package Money_IO is new Text_IO.Decimal_IO(Money);

   type Currency_Denomination is record
      Name : String(1..10);
      Value : Money;
   end record;

   Currency_Names  : array (1..10) of Currency_Denomination;
   Currency_Counts : array (1..10) of Integer := (others=>0);
   Currency_Index  : Integer range 1..10;

   procedure Get_Money_Prompt(Amount: out Money) is
      Response : String(1..20);
      Last     : Natural;
   begin
      loop
         declare
         begin
            Text_IO.Put("amount: ");
            Text_IO.Flush;
            Text_IO.Get_Line(Response, Last);
            -- try to convert string input to money type
            Amount := Money'Value(Response(1 .. Last));
            -- quit the loop if the money converted -- '
            exit;
         exception
            when Constraint_Error =>
               Text_IO.Put_Line("ERROR: bad money format");
         end;
      end loop;
   end Get_Money_Prompt;

begin
   Currency_Names := (("Penny     ", 0.01),
                      ("Nickle    ", 0.05),
                      ("Dime      ", 0.10),
                      ("Quarter   ", 0.25),
                      ("Dollar    ", 1.00),
                      ("5 Dollar  ", 5.00),
                      ("10 Dollar ", 10.00),
                      ("20 Dollar ", 20.00),
                      ("50 Dollar ", 50.00),
                      ("100 Dollar", 100.00));

   -- read money amount from the user
   Get_Money_Prompt(Input_Amount);

   -- calculate needed currency
   for I in Currency_Names'Range loop
      -- calculate inverse of index to count backwords
      Currency_Index := (Currency_Names'Length-I)+1;
      while Input_Amount >= Currency_Names(Currency_Index).Value loop
         Input_Amount := Input_Amount - Currency_Names(Currency_Index).Value;
         -- increment currency useage in array
         Currency_Counts(I) := Currency_Counts(I) + 1;
      end loop;
   end loop;

   -- display needed currency
   for I in Currency_Counts'Range loop
      Currency_Index := (Currency_Names'Length-I)+1;
      -- do not display unused currency
      if Currency_Counts(Currency_Index) /= 0 then
        Text_IO.Put(Currency_Names(I).Name & " ");
        Integer_Text_IO.Put(Currency_Counts(Currency_Index));
        Text_IO.New_Line;
      end if;
   end loop;

end Change_Calculator;
\$\endgroup\$
4
\$\begingroup\$

Currency_Names, Currency_Counts and Currency_Index all use the same range, so it is better to declare a ranged type (or a subtype):

   type Index is range 1..10;
   Currency_Names  : array (Index) of Currency_Denomination;
   Currency_Counts : array (Index) of Integer := (others=>0);
   Currency_Index  : Index;

To loop backwards, it is better to use the reverse keyword:

   -- calculate needed currency
   for I in reverse Currency_Names'Range loop
      while Input_Amount >= Currency_Names(I).Value loop
         Input_Amount := Input_Amount - Currency_Names(I).Value;
         -- increment currency useage in array
         Currency_Counts(I) := Currency_Counts(I) + 1;
      end loop;
   end loop;

   -- display needed currency
   for I in Currency_Counts'Range loop
      -- do not display unused currency
      if Currency_Counts(I) /= 0 then
        Text_IO.Put(Currency_Names(I).Name & " ");
        Integer_Text_IO.Put(Currency_Counts(I));
        Text_IO.New_Line;
      end if;
   end loop;

A nice side-effect is that the index of Currency_Counts will now correspond to the index of Currency_Names, no need to calculate the index anymore, and the Currency_Index variable is no longer needed.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.