我实现了一个.Net Web应用程序,它使用在Dafny中完全验证的.dll库。它运作良好,与图书馆的沟通并不困难。这真是棒极了。
不幸的是,代码的某些部分看起来不太好,我想问一下我是不是很好地使用了库,还是使用它的正确方法。我说的是数据类型。为了提出问题,我提供了一个简单的示例。
module DafnyCalculation
{
datatype Calculation = Sum(s1:int, s2:int) | Rest(r1:int, r2:int)
| Mult(m1:int, m2:int) | Div (d1:int, d2:int)
function method calculate(cal:Calculation): int
{
match cal
case Sum(s1,s2) => s1+s2
case Rest(r1,r2) => r1-r2
case Mult(m1,m2) => m1*m2
case Div(d1,d2) =>
if(d2!=0) then d1/d2
else d1
}
}
由于数据类型具有多个构造函数,因此在生成.dll时dafny会自动创建一些类:具有不同参数的Calculation,Base_Calculation,Calculation_Sum,Calculation_Rest,Calculation_Mult和Calculation_Div。我在C#控制台应用程序中以以下方式使用dll:
int result;
Base_Calculation cal;
Console.WriteLine("Enter first number: "); int x = int.Parse(Console.ReadLine());
Console.WriteLine("Enter second number: "); int y = int.Parse(Console.ReadLine());
Console.WriteLine("Choose operator:\n1)Sum\n2)Rest\n3)Mult\n4)Div\nOperator: ");
int op = int.Parse(Console.ReadLine());
switch (op)
{
case 1:
cal = new Calculation_Sum((BigInteger)x, (BigInteger)y);
break;
case 2:
cal = new Calculation_Rest((BigInteger)x, (BigInteger)y);
break;
case 3:
cal = new Calculation_Mult((BigInteger)x, (BigInteger)y);
break;
case 4 :
cal = new Calculation_Div((BigInteger)x, (BigInteger)y);
break;
default:
throw new Exception("Wrong option");
}
result = (int) _0_DafnyCalculation_Compile.__default.calculate(new Calculation(cal));
Console.WriteLine("Result: {0}", result);
Console.ReadLine();
基于示例,我有一些疑问:
是否有任何方法可以调用函数calculate(cal:Calculation),而不必构造新的Calculation对象并且直接包含一个“子类型”(Calculation_Sum,Calculation_Rest等)?
可以_0_DafnyCalculation_Compile .__ default。被避免?
是否需要导入System.Numerics并使用BigInteger转换C#int和Dafny int?还是可以通过其他方式完成?
非常感谢您。我试图保持说明性和清晰性,如果任何部分无法理解,请随时与我联系。
答案 0 :(得分:0)
感谢您的提问。
1)是的,现在有。今天,我检查了对编译器的更改,使您可以编写
Calculation.create_Sum(A, B)
代替以前的
new Calculation(new Calculation_Sum(A, B))
如果您的数据类型具有类型参数,则它们将紧随类型名称之后。
2)您可以通过通过_0_DafnyCalculation_Compile
提供自己的名称来避免使用{:extern YourNameGoesHere}
之类的名称混乱,该名称可以放在模块,类和方法之类的声明上。 (如果某些声明在您需要的地方不支持:extern
,请将其作为错误提交。)
(最近我一直在进行一些编译器更改。在很多情况下,我有可能能够自动删除_0_
前缀或_Compile
后缀。我们将看到。)
3)如果您使用Dafny的类型int
,则必须使用所提到的BigInteger
。但是,如果要使用本机整数类型,则可以在Dafny中声明自己的整数类型。例如,
newtype int32 = x | -0x8000_0000 <= x < 0x8000_0000
声明其值在给定范围内的Dafny类型int32
。 (在此示例中,x
的类型为int
,但是您也可以显式指定类型,例如newtype int32 = x: int | ...
。)Dafny编译器注意到此范围适合带符号的32位整数(在C#中写为int
),因此会将您的Dafny类型int32
编译为C#的int
。这适用于所有本机.NET整数类型。
如果由于某种原因您想要使用比范围所允许的更大的本机整数类型,则可以使用:nativeType
属性指示所需的内容。例如,
newtype {:nativeType "short"} byteStoredUsing16Bits = x | -128 <= x < 128
将使用.NET short
存储您的byteStoredUsing16Bits
类型。如果没有:nativeType
属性,Dafny编译器将为sbyte
选择.NET类型byteStoredUsing16Bits
。
您还可以将{:nativeType false}
与newtype
一起使用,以表示您不希望编译器使用本机整数(而是照常使用BigInteger
)。
请注意,newtype
声明允许您定义自己的整数类型,但是它们与Dafny的标准int
不兼容。与声明子集类型(通过使用关键字newtype
而非type
)来声明子集类型相反,使用newtype
的主要原因是允许编译器使用其他表示形式。如果需要在各种整数类型之间进行转换,请使用as
运算符。以下代码段说明了这一点:
var x: int := 70;
var y: int32 := x as int32;
var z: byteStoredUsing16Bits := y as byteStoredUsing16Bits;
x := z as int;
从数学上讲,他的as
运算符是部分身份函数。也就是说,as
永远不会更改值,验证程序将检查您的转换是否来自目标类型中存在的值。 (作为练习,将示例中的70
更改为700
,并观看验证者的抱怨。)
另外,请注意,newtype
类型的所有中间表达式也必须遵守您指定的任何谓词。
Rustan