小于某个上界的自然数。
具体而言,Fin n 是自然数 i,并带有约束 i < n;它是含有 n 个元素的规范类型。
对于任何自然数 n,Fin n 是一种包含所有严格小于 n 的自然数的类型。
换句话说,Fin n 恰好有 n 个元素。
它可用于表示列表或数组的有效索引,或者可用作规范的 n 元素类型。
Fin 与 UInt8、UInt16、UInt32、UInt64 和 USize 密切相关,它们也表示有限的非负整数类型。
然而,这些类型是由位向量而不是由自然数支持的,并且它们具有固定的边界。
Fin 相对更灵活,但在进行底层推理时不够方便。
特别是,使用位向量而不是证明某个数小于 2 的某个幂,可以避免必须小心翼翼地防止对具体边界求值的问题。
因为 Fin n 是一种只有一个字段不是证明的结构体,所以它是一个平凡包装器。
这意味着它在编译代码中的表示与底层的自然数相同。
从 Fin n 到 Nat 有一个强制转换,它会丢弃该数字小于边界的证明。
具体来说,这个强制转换正是投影 Fin.val。
这带来的一个后果是,Fin.val 的使用在证明状态中会显示为强制转换,而不是显式的投影。
自然数字面量可用于 Fin 类型,通常通过 OfNat 实例实现。
OfNat 为 Fin n 提供的实例要求上限 n 不为零,但不检查字面量是否小于 n。
如果字面量大于该类型所能表示的范围,则使用将其除以 n 的余数。
通常,对 Fin 的算术运算应该使用 Lean 的重载算术符号来访问,特别是通过实例 Add (Fin n)、Sub (Fin n)、Mul (Fin n)、Div (Fin n) 和 Mod (Fin n)。
异质运算符(例如 Fin.natAdd)没有对应的异质实例(例如 HAdd),以避免产生令人困惑的类型推断行为。
将自然数加到 Fin 上,同时增大上界。
这是 Fin.succ 的推广。
Fin.addNat 是此函数的另一版本,其 Nat 参数位于第二位。
示例:
Fin.natAdd 3 (5 : Fin 8) = (8 : Fin 11)
Fin.natAdd 1 (0 : Fin 8) = (1 : Fin 9)
Fin.natAdd 1 (2 : Fin 8) = (3 : Fin 9)
将自然数加到 Fin 上,同时增大上界。
这是 Fin.succ 的推广。
Fin.natAdd 是此函数的另一版本,其 Nat 参数位于第一位。
示例:
Fin.addNat (5 : Fin 8) 3 = (8 : Fin 11)
Fin.addNat (0 : Fin 8) 1 = (1 : Fin 9)
Fin.addNat (1 : Fin 8) 2 = (3 : Fin 10)
将上界替换为另一个适合该值的上界。
即使不知道具体值,也可利用嵌入 i 中的证明把它转换到更大的上界。
示例:
example : Fin 12 := (7 : Fin 10).castLT (⊢ 7 < 12 All goals completed! 🐙 : 7 < 12)
example (i : Fin 10) : Fin 12 :=
i.castLT <| i:Fin 10⊢ ↑i < 12
val✝:NatisLt✝:val✝ < 10⊢ ↑⟨val✝, isLt✝⟩ < 12; val✝:NatisLt✝:val✝ < 10⊢ val✝ < 12; All goals completed! 🐙
类型 Fin 0 无元素,因此可由它导出任意结果。
这类似于 Empty.elim。可将其看作由编译器检查的“代码路径不可达”断言,或看作一个逻辑矛盾:由此可推出 False,进而推出任何命题。
Fin.foldrM.{u_1, u_2} {m : Type u_1 → Type u_2} {α : Type u_1} [Monad m] (n : Nat) (f : Fin n → α → m α) (init : α) : m αFin.foldrM.{u_1, u_2} {m : Type u_1 → Type u_2} {α : Type u_1} [Monad m] (n : Nat) (f : Fin n → α → m α) (init : α) : m α
在 Fin n 上自右向左折叠单子函数,从 n-1 开始。
步骤顺序如下:
Fin.foldrM n f xₙ = do let xₙ₋₁ ← f (n-1) xₙ let xₙ₋₂ ← f (n-2) xₙ₋₁ ... let x₀ ← f 0 x₁ pure x₀
Fin.foldlM.{u_1, u_2} {m : Type u_1 → Type u_2} {α : Type u_1} [Monad m] (n : Nat) (f : α → Fin n → m α) (init : α) : m αFin.foldlM.{u_1, u_2} {m : Type u_1 → Type u_2} {α : Type u_1} [Monad m] (n : Nat) (f : α → Fin n → m α) (init : α) : m α
在 Fin n 的所有值上自左向右折叠单子函数,从 0 开始。
步骤顺序如下:
Fin.foldlM n f x₀ = do let x₁ ← f x₀ 0 let x₂ ← f x₁ 1 ... let xₙ ← f xₙ₋₁ (n-1) pure xₙ
把依赖索引的函数应用于所有小于给定上界 n 的值,从 0 和一个累加器开始。
具体而言,Fin.hIterate P init f 等于
init |> f 0 |> f 1 |> ... |> f (n-1)
关于 Fin.hIterate 的定理可用一般定理 Fin.hIterate_elim 或其他更专门的定理证明。
Fin.hIterateFrom 是一个变体,它接受自定义起始值而不总是从 0 开始。
把依赖索引的函数 f 应用于 [i:n] 中的所有值,从 i 和初始累加器 a 开始。
具体而言,Fin.hIterateFrom P f i a 等于
a |> f i |> f (i + 1) |> ... |> f (n - 1)
关于 Fin.hIterateFrom 的定理可用一般定理 Fin.hIterateFrom_elim 或其他更专门的定理证明。
Fin.hIterate 是一个始终从 0 开始的变体。
对底层 Nat 值归纳,以证明 Fin (n + 1) 中的一个命题。
归纳包含:
zero 是基本情形,证明 motive 0;
succ 是归纳步骤:假设动机对 i : Fin n 成立(提升到 Fin (n + 1) 时使用 Fin.castSucc),并证明它对 i.succ 成立。
Fin.inductionOn 是把 Fin 作为第一个参数的版本;Fin.cases 是相应的分类讨论算子;Fin.reverseInduction 则从最大值而非 0 开始。
对底层 Nat 值归纳,以证明 Fin (n + 1) 中的一个命题。
归纳包含:
zero 是基本情形,证明 motive 0;
succ 是归纳步骤:假设动机对 i : Fin n 成立(提升到 Fin (n + 1) 时使用 Fin.castSucc),并证明它对 i.succ 成立。
Fin.induction 是把 Fin 作为最后一个参数的版本。
Fin.addCases.{u} {m n : Nat} {motive : Fin (m + n) → Sort u} (left : (i : Fin m) → motive (Fin.castAdd n i)) (right : (i : Fin n) → motive (Fin.natAdd m i)) (i : Fin (m + n)) : motive iFin.addCases.{u} {m n : Nat} {motive : Fin (m + n) → Sort u} (left : (i : Fin m) → motive (Fin.castAdd n i)) (right : (i : Fin n) → motive (Fin.natAdd m i)) (i : Fin (m + n)) : motive i
i : Fin (m + n) 的分类讨论算子,分别处理 i < m 与 m ≤ i < m + n 两种情形。
第一种情形 i < m 由 left 处理;此时 i 可表示为 Fin.castAdd n (j : Fin m)。
第二种情形 m ≤ i < m + n 由 right 处理;此时 i 可表示为 Fin.natAdd m (j : Fin n)。
Fin 的归纳原理,把给定的 i : Fin n 看作连续应用 i 次 Fin.succ 所得。
归纳情形为:
与 Fin.induction 不同,这里的动机会量化上界,且上界随每个归纳步骤变化。Fin.succRecOn 是把 Fin 参数放在第一位的版本。
Fin 的归纳原理,把给定的 i : Fin n 看作连续应用 i 次 Fin.succ 所得。
归纳情形为:
与 Fin.induction 不同,这里的动机会量化上界,且上界随每个归纳步骤变化。Fin.succRec 是把 Fin 参数放在最后一位的版本。