Ada 匹配数组

Ada Matchup Array

我在 ada 中有一个函数,它必须将大量布尔值检查为 return 一个(稀疏)值。这很难用语言解释,所以这里有一个'ideal'在ada中不起作用的解决方案(注意我有超过3种挑战者类型和更严格的逻辑):

type ChallengerType is (Rock,Paper,Scissors,Suicide,None);
type Challengers is array (ChallengerType) of Boolean;
pragma Pack(Challengers);

-- NOT legal, challengers is not an enumeration type
matchups : array (Challengers) of ChallengerType := (
    -- Single challenger victories
    (Rock => True, others => False) => Rock,
    (Paper => True, others => False) => Paper,
    (Scissors => True, others => False) => Scissors,
    -- Double challenger victories
    (Rock => True, Paper => True, others => False) => Paper,
    (Rock => True, Scissors => True, others => False) => Rock,
    (Paper => True, Scissors => True, others => False) => Scissors,
    -- All the rest either are ambiguous (RPS) or suicided
    others => None)

这在 Ada 中是不合法的,所以我选择了更 c 风格的版本,其中我的数组是 Integer range 0..2#11111# 并编写了一个转换器。然而,代码变得不那么干净了(例如:(2#00101# => Scissors) 不那么清晰)。

'best' 实现这样一个对战数组的方法是什么?

总结:我想要一个从枚举类型的幂集到它的值之一的映射。 IE:如果我的枚举是 {A,B,C} 我想从 {{},{A},{B},{C},{A,B},{A,C},{B, C},{A,B,C}}到{A,B,C}。我也事先知道映射中的大多数值都是同一类型,因此 others 关键字非常好用。目前我使用带有“1”的二进制索引,这意味着指定的枚举元素存在,但我希望我能更明确。

不是答案...发帖以防有人看到前进的方向。

这扩展了我上面的评论:就从数组生成离散类型而言,它似乎有效...

with Ada.Unchecked_Conversion;

package RPS is

type ChallengerType is (Rock,Paper,Scissors,Suicide,None);
for ChallengerType use (Rock => 1, Paper => 2,Scissors => 4,Suicide => 8,None => 16);

type Challengers is array (ChallengerType) of Boolean with Pack;
type Challengers_int is range 0 .. 31;
function IDX is new Ada.Unchecked_Conversion(Challengers, Challengers_int);

Rock_Only : constant Challengers := (Rock => True, others => False);
Rock_IDX : constant Challengers_int := IDX(Rock_Only);

matchups : constant array (Challengers_int) of ChallengerType := (
    -- Single challenger victories
    -- Rock_IDX => Rock, -- fails
    1 => Rock,
    IDX((Paper => True, others => False)) => Paper, -- fails
    IDX((Scissors => True, others => False)) => Scissors,
    -- Double challenger victories
    IDX((Rock => True, Paper => True, others => False)) => Paper,
    IDX((Rock => True, Scissors => True, others => False)) => Rock,
    IDX((Paper => True, Scissors => True, others => False)) => Scissors,
    -- All the rest either are ambiguous (RPS) or suicided
    others => None);

end RPS;

但是,编译失败,dynamic or empty choice in aggregate must be the only choice 在数组中的任一注释行 - 即使索引是常量而不是表达式。

Case 语句同样失败:

case IDX(C) is 
when Rock_IDX => return Rock;
when IDX((Paper => True, others => False)) => return Paper;
...

编译器报告:

rps.adb:11:10: choice given in case statement is not static
rps.adb:11:10: "Rock_IDX" is not a static constant (RM 4.9(5))
rps.adb:12:10: choice given in case statement is not static
rps.adb:12:10: non-static function call (RM 4.9(6,18)) rps.adb:12:14:
static expression must have scalar or string type (RM 4.9(2))

这是一种解决方案:

with Ada.Text_IO; use Ada.text_IO;

procedure Mapping_Question is
   type ChallengerType is (Rock,Paper,Scissors,Suicide,None);
   type Challengers is array (ChallengerType range <>) of Boolean with Pack;
   Type Mapped_Challenges is array(Positive range <>) of ChallengerType;

   function Map_Challengers(Item : Challengers) return Mapped_Challenges is
      function Mapper(Item : Challengers) return Mapped_Challenges is
         Single : Mapped_Challenges(1..1);
      begin
         for I in Item'Range loop
            if Item(I) then
               if I < Item'Last then
                  return I & Mapper(Item(ChallengerType'Succ(I)..Item'Last));
               else
                  Single(Single'First) := I;
                  return Single;
               end if;
            end if;
         end loop;
         Single(Single'First) := None;
         return Single;
      end Mapper;
   begin
      return Mapper(Item);
   end Map_Challengers;

   procedure Print_Challenges(Item : Mapped_Challenges) is
   begin
      for I in Item'Range loop
         Put_Line(ChallengerType'Image(Item(I)));
      end loop;
   end Print_Challenges;

   Foo : Challengers(Rock..None) := (Rock..Scissors => True, Others => False);
begin
   declare
      Mapping : Mapped_Challenges := Map_Challengers(Foo);
   begin
      if Mapping'Length > 1 then
         Print_Challenges(Mapping(Mapping'First..Mapping'Last - 1));
      else
         Print_challenges(Mapping);
      end if;
   end;

end Mapping_Question;

一个更简单的答案使用 Ada.Containers.Vectors 包中的 Vector 类型:

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Vectors;
use Type Ada.Containers.Count_Type;

procedure Mapping_Vector is
   type ChallengerType is (Rock,Paper,Scissors,Suicide,None);
   type Challengers is array (ChallengerType) of Boolean with Pack;
   package Challenge_Vect is new Ada.Containers.Vectors(Positive, ChallengerType);
   use Challenge_Vect;

   function Map_Challengers(Item : in Challengers) return Vector is
      Result : Vector := Empty_Vector;
   begin
      for I in Item'Range loop
         if Item(I) then 
            Result.Append(I);
         end if;
      end loop;
      return Result;
   end Map_Challengers;

   Foo : Challengers := (Rock..Scissors => True, Others => False);
   Mapped_Challengers : Vector := Map_Challengers(Foo);
begin
   If Mapped_Challengers.Length > 0 then
      for C of Mapped_Challengers loop
         Put_Line(ChallengerType'Image(C));
      end loop;
   else
      Put_Line("No challengers were TRUE");
   end if;
end Mapping_Vector;

这不是一个纯静态的解决方案,但它可以根据枚举值优雅地配置映射,即使现实中的映射数组是由模块化类型索引的:

规格:

with Ada.Text_IO;
with Ada.Unchecked_Conversion;

generic
   type Element_Type is (<>);
   type Numeric_Type is mod <>;
package Set_With_Modular_Representation is
   type Instance is array (Element_Type) of Boolean with Pack;

   Empty_Set : constant Numeric_Type := 0;
   E         : Numeric_Type renames Empty_Set; --  Got a request not to use Ø.

   function "&" (Left  : in Instance;
                 Right : in Instance) return Numeric_Type;
   function "&" (Left  : in Numeric_Type;
                 Right : in Instance) return Numeric_Type;

   function "&" (Left  : in Element_Type;
                 Right : in Element_Type) return Numeric_Type;
   function "&" (Left  : in Numeric_Type;
                 Right : in Element_Type) return Numeric_Type;
private
   pragma Assert (Numeric_Type'Modulus = 2 ** Instance'Size);
   pragma Assert (Numeric_Type'Size = Instance'Size);

   function Numeric is
     new Ada.Unchecked_Conversion (Source => Instance,
                                   Target => Numeric_Type);
   function Numeric (Item : in Element_Type) return Numeric_Type;
end Set_With_Modular_Representation;

实施:

package body Set_With_Modular_Representation is
   function "&" (Left  : in Instance;
                 Right : in Instance) return Numeric_Type is
   begin
      return Numeric (Left) or Numeric (Right);
   end "&";

   function "&" (Left  : in Numeric_Type;
                 Right : in Instance) return Numeric_Type is
   begin
      return Left or Numeric (Right);
   end "&";

   function "&" (Left  : in Element_Type;
                 Right : in Element_Type) return Numeric_Type is
   begin
      return Numeric (Left) or Numeric (Right);
   end "&";

   function "&" (Left  : in Numeric_Type;
                 Right : in Element_Type) return Numeric_Type is
   begin
      return Left or Numeric (Right);
   end "&";

   function Numeric (Item : in Element_Type) return Numeric_Type is
      Buffer : Instance := (others => False);
   begin
      Buffer (Item) := True;
      return Numeric (Buffer);
   end Numeric;
end Set_With_Modular_Representation;

示范:

with Ada.Command_Line;
with Ada.Text_IO;

with Set_With_Modular_Representation;

procedure Set_With_Modular_Representation_Demo is
   type Outcomes is (Paper, Rock, Scissors, Suicide, None);
   subtype Choices is Outcomes range Paper .. Scissors;

   type Numeric_Choices is mod 2 ** 3;
   package Choice_Set is
     new Set_With_Modular_Representation (Element_Type => Choices,
                                          Numeric_Type => Numeric_Choices);
   use Choice_Set;

   Mapping : array (Numeric_Choices) of Outcomes := (others => None);
begin
   Set_Up_Mapping :
   begin
      --  Single challenger victories
      Mapping (E     & Rock)     := Rock;
      Mapping (E     & Paper)    := Paper;
      Mapping (E     & Scissors) := Scissors;

      --  Double challenger victories
      Mapping (Rock  & Paper)    := Paper;
      Mapping (Rock  & Scissors) := Rock;
      Mapping (Paper & Scissors) := Scissors;
   end Set_Up_Mapping;

   Test :
   declare
      package Outcome_Text_IO is
        new Ada.Text_IO.Enumeration_IO (Outcomes);
      use Ada.Command_Line, Ada.Text_IO, Outcome_Text_IO;

      Chosen : Numeric_Choices := E;
   begin
      for Index in 1 .. Argument_Count loop
         Chosen := Chosen & Choices'Value (Argument (Index)); --  '
      end loop;

      Put ("Outcome: ");
      Put (Mapping (Chosen));
      New_Line;
   end Test;
end Set_With_Modular_Representation_Demo;

以下方法使用了各种柯里化。为了进一步简化示例,我将枚举类型缩短为三个值。解决方案是静态的,但是直接使用枚举值的“优雅”已经消失了。如果不需要静态常量,那么,正如其他方法所示,常量函数表 FTChallengerTypeBoolean 将是可能的,然后命名组件显示为 T (Paper) => ...

pragma Pure (Rps);

type ChallengerType is (Rock,Paper,None);

type Challengers1 is array (Boolean) of Challengertype;
type Challengers2 is array (Boolean) of Challengers1;
type Challengers3 is array (Boolean) of Challengers2;

Matchups : constant Challengers3 :=
  -- Rock:
  (True => --> Paper:
      (True => --> None:
          (False => Paper,
           True => None),
       False => --> None:
         (others => Rock)),

   -- Rock:
   False => --> Paper:
     (True => --> None:
         (False => Paper,
          True => None),
      False =>
        (others => None))

  );

使用常量的解决方案

这不是一个优雅的解决方案,但作为更好的解决方案的一部分可能会有所帮助。

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with System;
with System.Unsigned_Types;

procedure Main is
   subtype S is System.Unsigned_Types.Unsigned range 0 .. 31;
   use type S;
   function Shift_Left (Value : S; Amount : Natural) return S renames System.Unsigned_Types.Shift_Left;
   Rock : constant S := Shift_Left (1, 0);
   Paper : constant S := Shift_Left (1, 1);
   Scissors : constant S := Shift_Left (1, 2);
   Suicide : constant S := Shift_Left (1, 3);
   None : constant S := Shift_Left (1, 4);
   P : array (S) of S := (others => None);
begin
   P (Rock or Paper) := Paper;
   P (Rock or Scissors) := Rock;
   P (Paper or Scissors) := Scissors;
   Put_Line ("P(Paper or Scissors) = Scissors");
   Put ("P(");
   Put (Integer (Paper), 0, 2);
   Put (" or ");
   Put (Integer (Scissors), 0, 2);
   Put (") = P(");
   Put (Integer (Paper or Scissors), 0, 2);
   Put (") = ");
   Put (Integer (P (Paper or Scissors)), 0, 2);
   Put ("");
end;

结果

P(Paper or Scissors) = Scissors
P(2#10# or 2#100#) = P(2#110#) = 2#100#

使用枚举的解决方案

with Ada.Text_IO; use Ada.Text_IO;
with System;
with System.Unsigned_Types;

procedure Main is
   type T is (Rock, Paper, Scissors, Suicide, None);
   subtype S is System.Unsigned_Types.Unsigned range 0 .. 31;
   use type S;
   function B (Value : T) return S is (System.Unsigned_Types.Shift_Left (1, T'Pos (Value)));
   function "&" (Left : T; Right : T) return S is (System.Unsigned_Types."or" (B (Left), B (Right)));
   P : array (S) of T := (others => None);
begin
   P (Rock & Paper) := Paper;
   P (Rock & Scissors) := Rock;
   P (Paper & Scissors) := Scissors;
   Put_Line (T'Image (P (Rock & Paper)));
   Put_Line (T'Image (P (Rock & Scissors)));
   Put_Line (T'Image (P (Paper & Scissors)));
end;

结果

PAPER
ROCK
SCISSORS