3

I'm trying to use a string in a case statement, however it is giving me expected a discrete type. Found type Standard.String I understand that strings are not discrete. I'm wondering if there is a work around or not. Here is my code:

function Is_Valid_Direction(Direction_To_Go : in String) return Integer is
  Room : Integer := 0;
   begin
      --if (Direction_To_Go = "NORTH" or Direction_To_Go = "N") then
      --   Room := Building(currentRoom).exits(NORTH);
      --elsif (Direction_To_Go = "SOUTH" or Direction_To_Go = "S") then
      --   Room := Building(currentRoom).exits(SOUTH);
      --elsif (Direction_To_Go = "EAST" or Direction_To_Go = "E") then
      --   Room := Building(currentRoom).exits(EAST);
      --elsif (Direction_To_Go = "WEST" or Direction_To_Go = "W") then
      --   Room := Building(currentRoom).exits(WEST);
      --elsif (Direction_To_Go = "UP" or Direction_To_Go = "U") then
      --   Room := Building(currentRoom).exits(UP);
      --elsif (Direction_To_Go = "DOWN" or Direction_To_Go = "D") then
      --   Room := Building(currentRoom).exits(DOWN);
      --end if;
      case Direction_To_Go is
         when "NORTH" | "N" => Room := Building(currentRoom).exits(NORTH);
         when "SOUTH" | "S" => Room := Building(currentRoom).exits(SOUTH);
         when "EAST" | "E" => Room := Building(currentRoom).exits(EAST);
         when "WEST" | "W" => Room := Building(currentRoom).exits(WEST);
         when "UP" | "U" => Room := Building(currentRoom).exits(UP);
         when "DOWN" | "D" => Room := Building(currentRoom).exits(DOWN);
         when others => Room := 0;
      end case;
      return Room;
   end Is_Valid_Direction;

The commented section is doing exactly what I want, but with if statements. I'm just trying to see if it's possible with a case statement.

NerdyFool
  • 531
  • 1
  • 7
  • 14

3 Answers3

7

You could map your strings to a discrete type. The easiest being an enumerated type:

procedure Light (Colour : in     String) is
   type Colours is (Red, Green, Blue);
begin
   case Colours'Value (Colour) is -- ' <- magic ;-)
      when Red =>
         Switch_Red_LED;
      when Green =>
         Switch_Green_LED;
      when Blue =>
         Switch_Blue_LED;
   end case;
exception
   when Constraint_Error =>
      raise Constraint_Error with "There is no " & Colour & " LED.";
end Light;
Jacob Sparre Andersen
  • 6,733
  • 17
  • 22
4

I frequently use an actual map to do this kind of mapping, as it gives you more flexibility than enumerations. Your "names" don't have to conform to enumeration syntax, and you can easily provide variations that all map to a single value.

For the desired function definition, as provided in a package:

package Case_Map is

   function Is_Valid_Direction(Direction_To_Go : in String) return Integer;

end Case_Map;

This (non-compiling due to missing game-specific declarations) implementation uses a mapping of strings to an enum that is in turn the case expression:

with Ada.Characters.Handling;
with Ada.Containers.Indefinite_Ordered_Maps;

package body Case_Map is

   use Ada.Characters.Handling;

   type Directions is (Go_North, Go_South, Go_East, Go_West, Go_Up, Go_Down);

   package Direction_Management is new Ada.Containers.Indefinite_Ordered_Maps
     (String, Directions);

   Direction_Map : Direction_Management.Map;

   function Is_Valid_Direction(Direction_To_Go : in String) return Integer is
      Room : Integer := 0;
   begin
      case Direction_Map(To_Upper(Direction_To_Go)) is
         when Go_North => Room := Building(CurrentRoom).Exits(NORTH);
         when Go_South => Room := Building(CurrentRoom).Exits(SOUTH);
         when Go_East => Room := Building(CurrentRoom).Exits(EAST);
         when Go_West => Room := Building(CurrentRoom).Exits(WEST);
         when Go_Up => Room := Building(CurrentRoom).Exits(UP);
         when Go_Down => Room := Building(CurrentRoom).Exits(DOWN);
      end case;
      return Room;
   exception
      when Constraint_Error =>
         return 0;
   end Is_Valid_Direction;

begin
   Direction_Map.Insert("NORTH", Go_North);
   Direction_Map.Insert("N", Go_North);
   Direction_Map.Insert("SOUTH", Go_South);
   Direction_Map.Insert("S", Go_South);
   Direction_Map.Insert("EAST", Go_East);
   Direction_Map.Insert("E", Go_East);
   Direction_Map.Insert("WEST", Go_West);
   Direction_Map.Insert("W", Go_West);
   Direction_Map.Insert("UP", Go_Up);
   Direction_Map.Insert("U", Go_Up);
   Direction_Map.Insert("DOWN", Go_Down);
   Direction_Map.Insert("D", Go_Down);
end Case_Map;
Marc C
  • 8,664
  • 1
  • 24
  • 29
0

The GNAT compiler itself uses a hash table that maps strings (identifiers, keywords,...) to integer. This is the package namet.ads, and GNATCOLL.Symbolic provides a similar API. This simplifies a number of things (string comparison for instance is much faster), and allow the use of case statements as in your example. So if you are using a limited (or at least slow-growing) list of strings, GNATCOLL.Symbolic might be a suitable approach

manuBriot
  • 2,755
  • 13
  • 21