简体   繁体   English

数组总数的 Spark-Ada 后置条件

[英]Spark-Ada postcondition for array total

How does one write a Spark postcondition for a function that sums the elements of an array?如何为对数组元素求和的函数编写 Spark 后置条件? (Spark 2014, but if someone shows me how to do it for an earlier Spark I should be able to adapt it.) (Spark 2014,但如果有人向我展示如何为早期的 Spark 做这件事,我应该能够适应它。)

So if I have:所以如果我有:

type Positive_Array is array (Positive range <>) of Positive;

function Array_Total(The_Array: Positive_Array) return Positive
with
  Post => Array_Total'Return = -- What goes here?
is
  -- and so on

I don't need to worry about overflow in my particular case (I know what the total was at initialisation, and it can only monotonically decrease).在我的特定情况下,我不需要担心溢出(我知道初始化时的总数是多少,它只能单调减少)。

Presumably I'll need a loop variant in the implementation, to help the prover, but that should be straightforward variation of the postcondition, so I'm not worried about that yet.大概我需要在实现中使用循环变体来帮助证明者,但这应该是后置条件的直接变体,所以我还不担心。

One way to write the postcondition could be as a recursive function.编写后置条件的一种方法可以是递归函数。 That would avoid the problem of the implementation and specification being exactly the same.这将避免实现和规范完全相同的问题。

This is an old, yet interesting question.这是一个古老但有趣的问题。 Here's a late answer, just for completeness and future reference.这是一个迟到的答案,仅供完整性和未来参考。

The main "trick" on how to solve these kind of problems was given in the blog post Taking on a Challenge in SPARK posted on AdaCore's website. AdaCore 网站上发布的博客文章“在 SPARK 中接受挑战”中给出了有关如何解决此类问题的主要“技巧”。

Opposed to what was already suggested by some of the answer's, you cannot use a recursive function to prove the summation.与某些答案已经提出的建议相反,您不能使用递归函数来证明求和。 Instead, you need a ghost function as shown in the example below.相反,您需要一个幽灵函数,如下例所示。 The method can be extended in order to proof similar "list folding" operations such as (conditional) counting.该方法可以扩展以证明类似的“列表折叠”操作,例如(条件)计数。

The example below can be proven with GNAT CE 2019 with proof level 1.下面的例子可以用 GNAT CE 2019 证明,证明级别为 1。

Update July 2020 2020 年 7 月更新

Small improvement in postcondition of Sum_Acc . Sum_Acc后置条件的小改进。 See also this related answer for another example.另请参阅相关答案的另一个示例。

sum.ads sum.ads

package Sum with SPARK_Mode is

   --  The ranges of the list's index and element discrete types must be
   --  limited in order to prevent overflow during summation, i.e.:
   --
   --     Nat'Last * Int'First >= Integer'First   and
   --     Nat'Last * Int'Last  <= Integer'Last
   --
   --  In this case +/-1000 * +/-1000 = +/-1_000_000 which is well within the 
   --  range of the Ada Integer type on typical platforms.
   
   subtype Int is Integer range -1000 .. 1000;
   subtype Nat is Integer range     0 .. 1000;
   
   type List is array (Nat range <>) of Int;
   
   
   --  The function "Sum_Acc" below is Ghost code to help the prover proof the
   --  postcondition (result) of the "Sum" function. It computes a list of
   --  partial sums. For example:
   --
   --     Input   :  [ 1  2  3  4  5  6 ]
   --     Output  :  [ 1  3  6 10 15 21 ]
   --
   --  Note that the lengths of lists are the same, the first elements are
   --  identical and the last element of the output (here: "21"), is the
   --  result of the actual function under consideration, "Sum".
   --
   --  REMARK: In this case, the input of "Sum_Acc" and "Sum" is limited
   --          to non-empty lists for convenience.
   
   type Partial_Sums is array (Nat range <>) of Integer;
   
   function Sum_Acc (L : List) return Partial_Sums with 
     Ghost,
     Pre  =>  (L'Length > 0),
     Post =>  (Sum_Acc'Result'Length = L'Length) 
     and then (Sum_Acc'Result'First = L'First) 
     and then (for all I in L'First .. L'Last =>
                 Sum_Acc'Result (I) in 
                   (I - L'First + 1) * Int'First .. 
                   (I - L'First + 1) * Int'Last)
     and then (Sum_Acc'Result (L'First) = L (L'First))
     and then (for all I in L'First + 1 .. L'Last =>
                 Sum_Acc'Result (I) = Sum_Acc'Result (I - 1) + L (I));
   
   
   function Sum (L : List) return Integer with
     Pre  => L'Length > 0,
     Post => Sum'Result = Sum_Acc (L) (L'Last);

end Sum;

sum.adb总和.adb

package body Sum with SPARK_Mode is

   -------------
   -- Sum_Acc --
   -------------
            
   function Sum_Acc (L : List) return Partial_Sums is
      PS : Partial_Sums (L'Range) := (others => 0);
   begin
      
      PS (L'First) := L (L'First);
      
      for Index in L'First + 1 .. L'Last loop
      
         --  Head equal.
         pragma Loop_Invariant
           (PS (L'First) = L (L'First));
         
         --  Tail equal.
         pragma Loop_Invariant
           (for all I in L'First + 1 .. Index - 1 =>
              PS (I) = PS (I - 1) + L (I)); 
         
         --  NOTE: The loop invariant below holds only when the range of "Int" 
         --        is symmetric, i.e -Int'First = Int'Last. If not, then this
         --        loop invariant may have to be adjusted.
         
         --  Result within bounds.
         pragma Loop_Invariant 
           (for all I in L'First .. Index - 1 =>
              PS (I) in (I - L'First + 1) * Int'First ..
                        (I - L'First + 1) * Int'Last);
               
         PS (Index) := PS (Index - 1) + L (Index);
      
      end loop;
      
      return PS;
      
   end Sum_Acc;

   ---------
   -- Sum --
   ---------
   
   function Sum (L : List) return Integer is
      Result : Integer := L (L'First);
   begin
      
      for I in L'First + 1 .. L'Last loop
         
         pragma Loop_Invariant
           (Result = Sum_Acc (L) (I - 1));
         
         Result := Result + L (I);
        
      end loop;
      
      return Result;
      
   end Sum;

end Sum;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM