"Taking on a Challenge in SPARK Ada" - 在 post 条件下求和幽灵函数 - 具有意外行为
"Taking on a Challenge in SPARK Ada" - Sum ghost function in post-condition having unintended behavior
我正在用 SPARK Ada 编写一个软件,它需要 post 条件来验证函数 return 值是否等于数组的求和值。在验证函数所在的文件后,我不断收到一个错误,该错误并没有完全加起来,没有双关语的意思(我将 post 代码的屏幕截图以便更好地查看)。大小为 10 的数组中唯一可接受的值是 0 或 1。
在下面的示例中(与 相反),我将计算部分和的 ghost 函数分离到通用 ghost 包 SPARK_Fold
中。从这个包中,我使用 ghost 函数 Sum_Acc
来证明 Calc_ST
中的求和循环。包 Example
可以使用 GNAT CE 2020 证明,证明级别设置为 1。
基础方法的学分:AdaCore blog post.
example.ads
with SPARK_Fold;
package Example with SPARK_Mode is
subtype EEG_Reading_Index is Integer range 0 .. Integer'Last - 1;
subtype EEG_Reading is Integer range 0 .. 1;
type EEG_Readings is array (EEG_Reading_Index range <>) of EEG_Reading;
package Sum_EEG_Readings is
new SPARK_Fold.Sum
(Index_Type => EEG_Reading_Index,
Element_In => EEG_Reading,
List_Type => EEG_Readings,
Element_Out => Natural);
function Calc_ST (EEG : EEG_Readings) return Natural with
Pre => EEG'Length > 0,
Post => Calc_ST'Result = Sum_EEG_Readings.Sum_Acc (EEG) (EEG'Last);
end Example;
example.adb(这里只是照常计算总和)。
package body Example with SPARK_Mode is
-------------
-- Calc_ST --
-------------
function Calc_ST (EEG : EEG_Readings) return Natural is
Result : Natural := EEG (EEG'First);
begin
for I in EEG'First + 1 .. EEG'Last loop
pragma Loop_Invariant
(Result = Sum_EEG_Readings.Sum_Acc (EEG) (I - 1));
Result := Result + EEG (I);
end loop;
return Result;
end Calc_ST;
end Example;
spark_fold.ads(通用帮助程序包)
package SPARK_Fold with Ghost is
-- Based on the blog post:
-- https://blog.adacore.com/taking-on-a-challenge-in-spark
---------
-- Sum --
---------
generic
type Index_Type is range <>;
type Element_In is range <>;
type List_Type is array (Index_Type range <>) of Element_In;
type Element_Out is range <>;
package Sum with Ghost is
type Partial_Sums is array (Index_Type range <>) of Element_Out;
function Sum_Acc (L : List_Type) 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 =>
abs (Sum_Acc'Result (I)) <= Element_Out (I - L'First + 1) * Element_Out (Element_In'Last))
and then (Sum_Acc'Result (L'First) = Element_Out (L (L'First)))
and then (for all I in L'First + 1 .. L'Last =>
Sum_Acc'Result (I) = Sum_Acc'Result (I - 1) + Element_Out (L (I)));
end Sum;
-----------
-- Count --
-----------
generic
type Index_Type is range <>;
type Element is range <>;
type List_Type is array (Index_Type range <>) of Element;
with function Choose (X : Element) return Boolean;
-- Count the number of elements for which Choose returns True.
package Count with Ghost is
type Partial_Counts is array (Index_Type range <>) of Natural;
function Count_Acc (L : List_Type) return Partial_Counts with
Ghost,
Pre => (L'Length > 0),
Post => (Count_Acc'Result'Length = L'Length)
and then (Count_Acc'Result'First = L'First)
and then (for all I in L'First .. L'Last =>
Count_Acc'Result (I) <= Natural (I) - Natural (L'First) + 1)
and then (Count_Acc'Result (L'First) = (if Choose (L (L'First)) then 1 else 0))
and then (for all I in L'First + 1 .. L'Last =>
Count_Acc'Result (I) = Count_Acc'Result (I - 1) + (if Choose (L (I)) then 1 else 0));
end Count;
end SPARK_Fold;
spark_fold.adb
package body SPARK_Fold is
---------
-- Sum --
---------
package body Sum is
function Sum_Acc (L : List_Type) return Partial_Sums is
Result : Partial_Sums (L'Range) := (others => 0);
begin
Result (L'First) := Element_Out (L (L'First));
for Index in L'First + 1 .. L'Last loop
-- Head equal.
pragma Loop_Invariant
(Result (L'First) = Element_Out (L (L'First)));
-- Tail equal.
pragma Loop_Invariant
(for all I in L'First + 1 .. Index - 1 =>
Result (I) = Result (I - 1) + Element_Out (L (I)));
-- Result within bounds.
pragma Loop_Invariant
(for all I in L'First .. Index - 1 =>
abs (Result (I)) <= Element_Out (I - L'First + 1) * Element_Out (Element_In'Last));
Result (Index) := Result (Index - 1) + Element_Out (L (Index));
end loop;
return Result;
end Sum_Acc;
end Sum;
-----------
-- Count --
-----------
package body Count is
function Count_Acc (L : List_Type) return Partial_Counts is
Result : Partial_Counts (L'Range) := (others => 0);
begin
if Choose (L (L'First)) then
Result (L'First) := 1;
else
Result (L'First) := 0;
end if;
for Index in L'First + 1 .. L'Last loop
-- Head equal.
pragma Loop_Invariant
(Result (L'First) = (if Choose (L (L'First)) then 1 else 0));
-- Tail equal.
pragma Loop_Invariant
(for all I in L'First + 1 .. Index - 1 =>
Result (I) = Result (I - 1) + (if Choose (L (I)) then 1 else 0));
-- Bounds.
pragma Loop_Invariant
(for all I in L'First .. Index - 1 =>
Result (I) <= Natural (I) - Natural (L'First) + 1);
if Choose (L (Index)) then
Result (Index) := Result (Index - 1) + 1;
else
Result (Index) := Result (Index - 1) + 0;
end if;
end loop;
return Result;
end Count_Acc;
end Count;
end SPARK_Fold;
这是修复:
function CalcST(eegR: in eegReadings) return Natural is
supT: Integer := eegR(eegR'First);
begin
-- Sums the number of ones in the array
for Index in eegR'First + 1 .. eegR'Last loop
pragma Loop_Invariant --
(supT = sumEEGR (eegR) (Index - 1));
pragma Loop_Invariant -- additional loop invariant
(supT <= Index - 1);
if eegR(Index) = 1
then supT := supT + eegR(Index);
end if;
end loop;
return supT;
end CalcST;
我正在用 SPARK Ada 编写一个软件,它需要 post 条件来验证函数 return 值是否等于数组的求和值。在验证函数所在的文件后,我不断收到一个错误,该错误并没有完全加起来,没有双关语的意思(我将 post 代码的屏幕截图以便更好地查看)。大小为 10 的数组中唯一可接受的值是 0 或 1。
在下面的示例中(与 SPARK_Fold
中。从这个包中,我使用 ghost 函数 Sum_Acc
来证明 Calc_ST
中的求和循环。包 Example
可以使用 GNAT CE 2020 证明,证明级别设置为 1。
基础方法的学分:AdaCore blog post.
example.ads
with SPARK_Fold;
package Example with SPARK_Mode is
subtype EEG_Reading_Index is Integer range 0 .. Integer'Last - 1;
subtype EEG_Reading is Integer range 0 .. 1;
type EEG_Readings is array (EEG_Reading_Index range <>) of EEG_Reading;
package Sum_EEG_Readings is
new SPARK_Fold.Sum
(Index_Type => EEG_Reading_Index,
Element_In => EEG_Reading,
List_Type => EEG_Readings,
Element_Out => Natural);
function Calc_ST (EEG : EEG_Readings) return Natural with
Pre => EEG'Length > 0,
Post => Calc_ST'Result = Sum_EEG_Readings.Sum_Acc (EEG) (EEG'Last);
end Example;
example.adb(这里只是照常计算总和)。
package body Example with SPARK_Mode is
-------------
-- Calc_ST --
-------------
function Calc_ST (EEG : EEG_Readings) return Natural is
Result : Natural := EEG (EEG'First);
begin
for I in EEG'First + 1 .. EEG'Last loop
pragma Loop_Invariant
(Result = Sum_EEG_Readings.Sum_Acc (EEG) (I - 1));
Result := Result + EEG (I);
end loop;
return Result;
end Calc_ST;
end Example;
spark_fold.ads(通用帮助程序包)
package SPARK_Fold with Ghost is
-- Based on the blog post:
-- https://blog.adacore.com/taking-on-a-challenge-in-spark
---------
-- Sum --
---------
generic
type Index_Type is range <>;
type Element_In is range <>;
type List_Type is array (Index_Type range <>) of Element_In;
type Element_Out is range <>;
package Sum with Ghost is
type Partial_Sums is array (Index_Type range <>) of Element_Out;
function Sum_Acc (L : List_Type) 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 =>
abs (Sum_Acc'Result (I)) <= Element_Out (I - L'First + 1) * Element_Out (Element_In'Last))
and then (Sum_Acc'Result (L'First) = Element_Out (L (L'First)))
and then (for all I in L'First + 1 .. L'Last =>
Sum_Acc'Result (I) = Sum_Acc'Result (I - 1) + Element_Out (L (I)));
end Sum;
-----------
-- Count --
-----------
generic
type Index_Type is range <>;
type Element is range <>;
type List_Type is array (Index_Type range <>) of Element;
with function Choose (X : Element) return Boolean;
-- Count the number of elements for which Choose returns True.
package Count with Ghost is
type Partial_Counts is array (Index_Type range <>) of Natural;
function Count_Acc (L : List_Type) return Partial_Counts with
Ghost,
Pre => (L'Length > 0),
Post => (Count_Acc'Result'Length = L'Length)
and then (Count_Acc'Result'First = L'First)
and then (for all I in L'First .. L'Last =>
Count_Acc'Result (I) <= Natural (I) - Natural (L'First) + 1)
and then (Count_Acc'Result (L'First) = (if Choose (L (L'First)) then 1 else 0))
and then (for all I in L'First + 1 .. L'Last =>
Count_Acc'Result (I) = Count_Acc'Result (I - 1) + (if Choose (L (I)) then 1 else 0));
end Count;
end SPARK_Fold;
spark_fold.adb
package body SPARK_Fold is
---------
-- Sum --
---------
package body Sum is
function Sum_Acc (L : List_Type) return Partial_Sums is
Result : Partial_Sums (L'Range) := (others => 0);
begin
Result (L'First) := Element_Out (L (L'First));
for Index in L'First + 1 .. L'Last loop
-- Head equal.
pragma Loop_Invariant
(Result (L'First) = Element_Out (L (L'First)));
-- Tail equal.
pragma Loop_Invariant
(for all I in L'First + 1 .. Index - 1 =>
Result (I) = Result (I - 1) + Element_Out (L (I)));
-- Result within bounds.
pragma Loop_Invariant
(for all I in L'First .. Index - 1 =>
abs (Result (I)) <= Element_Out (I - L'First + 1) * Element_Out (Element_In'Last));
Result (Index) := Result (Index - 1) + Element_Out (L (Index));
end loop;
return Result;
end Sum_Acc;
end Sum;
-----------
-- Count --
-----------
package body Count is
function Count_Acc (L : List_Type) return Partial_Counts is
Result : Partial_Counts (L'Range) := (others => 0);
begin
if Choose (L (L'First)) then
Result (L'First) := 1;
else
Result (L'First) := 0;
end if;
for Index in L'First + 1 .. L'Last loop
-- Head equal.
pragma Loop_Invariant
(Result (L'First) = (if Choose (L (L'First)) then 1 else 0));
-- Tail equal.
pragma Loop_Invariant
(for all I in L'First + 1 .. Index - 1 =>
Result (I) = Result (I - 1) + (if Choose (L (I)) then 1 else 0));
-- Bounds.
pragma Loop_Invariant
(for all I in L'First .. Index - 1 =>
Result (I) <= Natural (I) - Natural (L'First) + 1);
if Choose (L (Index)) then
Result (Index) := Result (Index - 1) + 1;
else
Result (Index) := Result (Index - 1) + 0;
end if;
end loop;
return Result;
end Count_Acc;
end Count;
end SPARK_Fold;
这是修复:
function CalcST(eegR: in eegReadings) return Natural is
supT: Integer := eegR(eegR'First);
begin
-- Sums the number of ones in the array
for Index in eegR'First + 1 .. eegR'Last loop
pragma Loop_Invariant --
(supT = sumEEGR (eegR) (Index - 1));
pragma Loop_Invariant -- additional loop invariant
(supT <= Index - 1);
if eegR(Index) = 1
then supT := supT + eegR(Index);
end if;
end loop;
return supT;
end CalcST;