协作头脑风暴
brainstorming
在实现前澄清需求、探索设计选择,并形成清晰的开发方向。
python-to-dafny-translator【开发与工程】Translate Python programs into equivalent Dafny code, preserving program semantics and ensuring the generated code is well-typed, executable, and verifiable. Use when the user asks to convert Python code to Dafny, port Python programs to Dafny, add formal verification to Python code, or create Dafny versions of Python algorithms with specifications.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Transform Python programs into equivalent Dafny code that preserves the original semantics while adding formal specifications and leveraging Dafny's verification capabilities.
Understand the Python program structure and semantics:
Identify program components:
Understand semantics:
Note translation challenges:
Plan the Dafny equivalent:
Choose appropriate types:
int for integers (arbitrary precision)nat for non-negative integersbool for booleansstring for stringsreal for floating-point (when needed)seq<T> for listsset<T> for setsmap<K, V> for dictionariesarray<T> for mutable arraysDetermine function vs method:
Plan specifications:
requires)ensures)modifies, reads)Follow these translation principles:
Pattern: Pure function
# Python
def add(a, b):
return a + b
// Dafny
function add(a: int, b: int): int
{
a + b
}
Pattern: Function with specifications
# Python
def max_value(a, b):
"""Returns the maximum of a and b"""
return a if a > b else b
// Dafny
function max(a: int, b: int): int
ensures max(a, b) >= a && max(a, b) >= b
ensures max(a, b) == a || max(a, b) == b
{
if a > b then a else b
}
Pattern: Method with side effects
# Python
def increment_counter(counter):
counter[0] += 1
// Dafny
method incrementCounter(counter: array<int>)
requires counter.Length > 0
modifies counter
ensures counter[0] == old(counter[0]) + 1
{
counter[0] := counter[0] + 1;
}
Pattern: If-else
# Python
def abs_value(x):
if x < 0:
return -x
else:
return x
// Dafny
function abs(x: int): int
ensures abs(x) >= 0
{
if x < 0 then -x else x
}
Pattern: For loop → While loop with invariants
# Python
def sum_array(arr):
total = 0
for i in range(len(arr)):
total += arr[i]
return total
// Dafny
method sumArray(arr: array<int>) returns (total: int)
{
total := 0;
var i := 0;
while i < arr.Length
invariant 0 <= i <= arr.Length
invariant total == sum(arr[..i])
{
total := total + arr[i];
i := i + 1;
}
}
function sum(s: seq<int>): int
{
if |s| == 0 then 0 else s[0] + sum(s[1..])
}
Pattern: Lists → Sequences
# Python
lst = [1, 2, 3, 4, 5]
first = lst[0]
length = len(lst)
// Dafny
var lst: seq<int> := [1, 2, 3, 4, 5];
var first: int := lst[0];
var length: int := |lst|;
Pattern: Dictionaries → Maps
# Python
d = {"a": 1, "b": 2}
value = d["a"]
// Dafny
var d: map<string, int> := map["a" := 1, "b" := 2];
var value: int := d["a"];
Pattern: Simple class
# Python
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def move(self, dx, dy):
self.x += dx
self.y += dy
// Dafny
class Point {
var x: int
var y: int
constructor(x: int, y: int)
ensures this.x == x && this.y == y
{
this.x := x;
this.y := y;
}
method move(dx: int, dy: int)
modifies this
ensures x == old(x) + dx
ensures y == old(y) + dy
{
x := x + dx;
y := y + dy;
}
}
Enhance the Dafny code with formal specifications:
Preconditions (requires):
requires n >= 0 for factorialPostconditions (ensures):
ensures result >= 0 for absolute valueLoop invariants:
invariant 0 <= i <= n for loop counterFrame conditions:
modifies: What can be modified?reads: What can be read?Ensure the translated code is correct:
Run Dafny verifier:
dafny verify program.dfy
Fix verification errors:
Test execution:
dafny run program.dfy
Compare outputs:
Improve the translated code:
Simplify specifications:
Add documentation:
// Computes the factorial of n
function factorial(n: nat): nat
Optimize for verification:
For detailed patterns, see translation_patterns.md.
| Python | Dafny |
|---|---|
x = 10 | var x: int := 10; |
def f(x): | function f(x: int): int or method f(x: int) |
return x | x (function) or return x; (method) |
if x > 0: | if x > 0 then or if x > 0 { |
for i in range(n): | while i < n with invariants |
[1, 2, 3] | [1, 2, 3] (seq) |
{1, 2, 3} | {1, 2, 3} (set) |
{"a": 1} | map["a" := 1] |
len(lst) | |lst| |
lst[i] | lst[i] |
class C: | class C { |
None | Use Option<T> datatype |
Python Input:
def factorial(n):
if n <= 1:
return 1
return n * factorial(n - 1)
Dafny Output:
function factorial(n: nat): nat
ensures factorial(n) >= 1
{
if n <= 1 then 1
else n * factorial(n - 1)
}
Python Input:
def find_max(arr):
if len(arr) == 0:
return None
max_val = arr[0]
for i in range(1, len(arr)):
if arr[i] > max_val:
max_val = arr[i]
return max_val
Dafny Output:
method findMax(arr: array<int>) returns (maxVal: int)
requires arr.Length > 0
ensures forall k :: 0 <= k < arr.Length ==> maxVal >= arr[k]
ensures exists k :: 0 <= k < arr.Length && maxVal == arr[k]
{
maxVal := arr[0];
var i := 1;
while i < arr.Length
invariant 1 <= i <= arr.Length
invariant forall k :: 0 <= k < i ==> maxVal >= arr[k]
invariant exists k :: 0 <= k < i && maxVal == arr[k]
{
if arr[i] > maxVal {
maxVal := arr[i];
}
i := i + 1;
}
}
Python Input:
class BankAccount:
def __init__(self, balance):
self.balance = balance
def deposit(self, amount):
self.balance += amount
def withdraw(self, amount):
if amount <= self.balance:
self.balance -= amount
return True
return False
Dafny Output:
class BankAccount {
var balance: int
constructor(balance: int)
requires balance >= 0
ensures this.balance == balance
{
this.balance := balance;
}
method deposit(amount: int)
requires amount >= 0
modifies this
ensures balance == old(balance) + amount
{
balance := balance + amount;
}
method withdraw(amount: int) returns (success: bool)
requires amount >= 0
modifies this
ensures success ==> balance == old(balance) - amount
ensures !success ==> balance == old(balance)
ensures success <==> amount <= old(balance)
{
if amount <= balance {
balance := balance - amount;
return true;
}
return false;
}
}
Python Input:
def binary_search(arr, target):
left, right = 0, len(arr) - 1
while left <= right:
mid = (left + right) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1
Dafny Output:
method binarySearch(arr: array<int>, target: int) returns (index: int)
requires forall i, j :: 0 <= i < j < arr.Length ==> arr[i] <= arr[j]
ensures 0 <= index < arr.Length ==> arr[index] == target
ensures index == -1 ==> forall k :: 0 <= k < arr.Length ==> arr[k] != target
{
var left := 0;
var right := arr.Length - 1;
while left <= right
invariant 0 <= left <= arr.Length
invariant -1 <= right < arr.Length
invariant forall k :: 0 <= k < left ==> arr[k] < target
invariant forall k :: right < k < arr.Length ==> arr[k] > target
{
var mid := (left + right) / 2;
if arr[mid] == target {
return mid;
} else if arr[mid] < target {
left := mid + 1;
} else {
right := mid - 1;
}
}
return -1;
}
nat for non-negative values, seq for immutable listsNone, Dafny uses Option<T> datatype