Lean 函数式编程

8.5. 安全数组索引🔗

ArrayNatGetElem 实例需要证明提供的 Nat 小于数组。 在实践中,这些证明通常最终会连同索引一起传递给函数。 与其分别传递索引和证明,可以使用名为 Fin 的类型将索引和证明捆绑到单个值中。 这可以使代码更易阅。

类型 Fin n 表示严格小于 n 的数字。 换句话说,Fin 3 描述 012,而 Fin 0 没有任何值。 Fin 的定义类似于 Subtype,因为 Fin n 是一个包含 Nat 和小于 n 的证明的结构体:

structure Fin (n : Nat) where val : Nat isLt : LT.lt val n

Lean 包含 ToStringOfNat 的实例,允许将 Fin 值方便地用作数字。 换句话说,#eval (5 : Fin 8) 的输出为 5,而非类似 {val := 5, isLt := _} 的值。

当提供的数字大于边界时,FinOfNat 实例不会失败,而是返回一个对边界取模的值。 这意味着 #eval (45 : Fin 10) 的结果是 5,而非编译时错误。

在返回类型中,将 Fin 作为找到的索引返回,能够让它与其所在的数据结构的连接更加清晰。 上一节中的 Array.find 返回一个索引,调用者不能立即使用它来执行数组查找,因为有关其有效性的信息已丢失。 更具体类型的值可以直接使用,而不会使程序变得复杂得多:

def findHelper (arr : Array α) (p : α Bool) (i : Nat) : Option (Fin arr.size × α) := if h : i < arr.size then let x := arr[i] if p x then some (i, h, x) else findHelper arr p (i + 1) else nonedef Array.find (arr : Array α) (p : α Bool) : Option (Fin arr.size × α) := findHelper arr p 0

8.5.1. 练习🔗

编写一个函数 Fin.next? : Fin n Option (Fin n)Fin 在边界内时返回下一个最大的 Fin,否则返回 none。 检查

some 4#eval (3 : Fin 8).next?

输出

some 4

并且

none#eval (7 : Fin 8).next?

输出

none