跳转到内容

Lean递归函数:修订间差异

来自代码酷
Admin留言 | 贡献
Page creation by admin bot
 
Admin留言 | 贡献
Page update by admin bot
 
第1行: 第1行:
= Lean递归函数 =
= Lean递归函数 =


'''Lean递归函数'''是Lean函数式编程中的核心概念之一,它允许函数通过调用自身来解决复杂问题。递归在数学和计算机科学中广泛应用,尤其在处理递归数据结构(如列表、树等)时尤为高效。本节将详细介绍Lean中的递归函数,包括其基本语法、终止性保证、模式匹配以及实际应用案例。
'''Lean递归函数'''是Lean定理证明器中实现重复计算的核心构造,它允许函数通过调用自身来解决问题。递归在函数式编程和数学归纳证明中具有基础性地位,是处理归纳定义数据类型(如自然数、列表、树等)的必备工具。


== 递归函数的基本概念 ==
== 基本概念 ==


递归函数是指在函数定义中调用自身的函数。在Lean中,递归函数必须满足'''终止性'''(Termination),即函数调用最终会停止,否则会导致无限循环。Lean的类型系统会检查递归函数的终止性,确保程序的正确性。
在Lean中,递归函数必须满足'''终止性'''(Termination)条件,即所有递归调用必须作用于"更小"的参数,以确保计算最终会停止。这与数学归纳法的原理紧密相关。


递归通常用于解决可以分解为更小子问题的问题,例如计算阶乘、斐波那契数列或遍历树结构。
递归函数的定义通常包含两个部分:
* '''基例'''(Base Case):处理最简单的情况,无需递归
* '''递归情况'''(Recursive Case):将问题分解为更小的子问题


=== 递归函数的语法 ===
=== 简单示例:阶乘函数 ===


在Lean中,递归函数使用`def`关键字定义,并通过`:=`指定函数体。递归调用必须满足结构递归(Structural Recursion)或提供明确的终止证明。
以下是用Lean定义的阶乘函数:


<syntaxhighlight lang="lean">
<syntaxhighlight lang="lean">
-- 计算阶乘的递归函数
def factorial : Nat → Nat
def factorial : Nat → Nat
  | 0 => 1  -- 基本情况
| 0     => 1  -- 基例:0! = 1
  | (n + 1) => (n + 1) * factorial n  -- 递归情况
| n + 1 => (n + 1) * factorial n  -- 递归情况:(n+1)! = (n+1) * n!
</syntaxhighlight>
</syntaxhighlight>


'''输入示例:'''
'''输入输出示例'''
<syntaxhighlight lang="lean">
* `#eval factorial 5` → 120
#eval factorial 5 -- 输出: 120
* `#eval factorial 0` → 1
</syntaxhighlight>


'''解释:'''
== 结构递归与良基递归 ==
* 当输入为`0`时,函数返回`1`(基本情况)。
* 当输入为`n + 1`时,函数递归计算`factorial n`,并将结果乘以`n + 1`(递归情况)。


== 结构递归与终止性 ==
Lean主要支持两种安全的递归形式:


Lean要求递归函数必须是'''结构递归'''的,即递归调用必须作用于原始参数的严格子结构。例如,在列表递归中,递归调用必须作用于列表的尾部。
=== 1. 结构递归 ===
递归调用只作用于参数的直接子结构,这是最简单的形式:


<syntaxhighlight lang="lean">
<syntaxhighlight lang="lean">
-- 计算列表长度的递归函数
def list_length {α : Type} : List α → Nat
def length {α : Type} : List α → Nat
| []     => 0
  | [] => 0 -- 空列表长度为0
| _ :: xs => 1 + list_length xs
  | _ :: xs => 1 + length xs -- 非空列表长度为1加上尾部长度
</syntaxhighlight>
</syntaxhighlight>


'''输入示例:'''
=== 2. 良基递归 ===
当结构递归不适用时,可以使用'''well-founded recursion''',它依赖一个显式的终止证明:
 
<syntaxhighlight lang="lean">
<syntaxhighlight lang="lean">
#eval length [1, 2, 3]  -- 输出: 3
def ackermann : Nat → Nat → Nat
| 0,  n  => n + 1
| m+1, 0  => ackermann m 1
| m+1, n+1 => ackermann m (ackermann (m + 1) n)
termination_by ackermann m n => (m, n)
</syntaxhighlight>
</syntaxhighlight>


'''解释:'''
== 递归原理图 ==
* 空列表`[]`的长度为`0`。
* 非空列表`_ :: xs`的长度为`1`加上`xs`的长度(递归调用)。


=== 终止性证明 ===
<mermaid>
graph TD
    A[递归函数调用] -->|参数更小| B[递归调用]
    B --> C[继续分解]
    C -->|达到基例| D[返回结果]
    D --> E[组合结果]
</mermaid>


如果递归不是结构递归,Lean会要求提供终止性证明。例如,以下函数使用`termination_by`指定终止条件:
== 递归与归纳的关系 ==
 
<syntaxhighlight lang="lean">
-- 使用终止性证明的递归函数
def ackermann : Nat → Nat → Nat
  | 0, n => n + 1
  | m + 1, 0 => ackermann m 1
  | m + 1, n + 1 => ackermann m (ackermann (m + 1) n)
termination_by ackermann m n => (m, n)
</syntaxhighlight>


'''输入示例:'''
在Lean中,递归和归纳是紧密相关的概念。递归函数的正确性通常需要用归纳法来证明。例如,证明`factorial`函数的正确性:
<syntaxhighlight lang="lean">
#eval ackermann 2 1  -- 输出: 5
</syntaxhighlight>


'''解释:'''
<math>
* `termination_by`指定了终止条件,确保递归调用最终停止。
\text{factorial}(n) = \prod_{k=1}^n k
</math>


== 递归与模式匹配 ==
== 实际应用案例 ==


Lean支持通过模式匹配(Pattern Matching)简化递归函数的定义。模式匹配可以同时处理多种输入情况,使代码更清晰。
=== 案例1:二叉树遍历 ===


<syntaxhighlight lang="lean">
<syntaxhighlight lang="lean">
-- 使用模式匹配的递归函数(计算斐波那契数列)
inductive BinaryTree (α : Type) where
def fib : Nat → Nat
| leaf : BinaryTree α
  | 0 => 0
| node : BinaryTree α → α → BinaryTree α → BinaryTree α
  | 1 => 1
 
  | (n + 2) => fib (n + 1) + fib n
def treeDepth {α : Type} : BinaryTree α → Nat
| .leaf => 0
| .node l _ r => 1 + max (treeDepth l) (treeDepth r)
</syntaxhighlight>
</syntaxhighlight>


'''输入示例:'''
=== 案例2:快速排序 ===
 
<syntaxhighlight lang="lean">
<syntaxhighlight lang="lean">
#eval fib 6  -- 输出: 8
def quicksort : List Int → List Int
| [] => []
| x :: xs =>
    let smaller := quicksort (xs.filter (· ≤ x))
    let larger := quicksort (xs.filter (· > x))
    smaller ++ [x] ++ larger
</syntaxhighlight>
</syntaxhighlight>


'''解释:'''
== 常见问题与调试 ==
* `fib 0`返回`0`,`fib 1`返回`1`。
* 对于`n + 2`,递归计算`fib (n + 1)`和`fib n`,并求和。


== 递归的实际应用 ==
1. '''非终止递归'''错误:
  - 确保每次递归调用参数都在减小
  - 使用`termination_by`子句显式指定终止度量


递归在函数式编程中广泛应用,以下是一些实际案例:
2. '''效率问题''':
  - 避免重复计算(可考虑记忆化)
  - 尾递归优化(Lean会自动优化部分情况)


=== 1. 列表反转 ===
== 高级主题 ==
<syntaxhighlight lang="lean">
def reverse {α : Type} : List α → List α
  | [] => []
  | x :: xs => reverse xs ++ [x]
</syntaxhighlight>


'''输入示例:'''
=== 互递归 ===
<syntaxhighlight lang="lean">
多个函数相互调用:
#eval reverse [1, 2, 3]  -- 输出: [3, 2, 1]
</syntaxhighlight>


=== 2. 二叉树遍历 ===
<syntaxhighlight lang="lean">
<syntaxhighlight lang="lean">
inductive Tree (α : Type) where
mutual
  | empty : Tree α
def even : Nat Bool
  | node (left : Tree α) (val : α) (right : Tree α) : Tree α
| 0 => true
 
| n + 1 => odd n
def size {α : Type} : Tree α Nat
  | Tree.empty => 0
  | Tree.node l _ r => 1 + size l + size r
</syntaxhighlight>


'''输入示例:'''
def odd : Nat → Bool
<syntaxhighlight lang="lean">
| 0 => false
def sampleTree := Tree.node (Tree.node Tree.empty 1 Tree.empty) 2 (Tree.node Tree.empty 3 Tree.empty)
| n + 1 => even n
#eval size sampleTree  -- 输出: 3
end
</syntaxhighlight>
</syntaxhighlight>


=== 3. 快速排序 ===
=== 递归定理证明 ===
<syntaxhighlight lang="lean">
递归不仅用于计算,也用于证明:
def qsort : List Nat → List Nat
  | [] => []
  | x :: xs =>
    let smaller := qsort (xs.filter (· ≤ x))
    let larger := qsort (xs.filter (· > x))
    smaller ++ [x] ++ larger
</syntaxhighlight>


'''输入示例:'''
<syntaxhighlight lang="lean">
<syntaxhighlight lang="lean">
#eval qsort [5, 2, 9, 1, 3]  -- 输出: [1, 2, 3, 5, 9]
theorem factorial_pos : ∀ n, 0 < factorial n
| 0    => by simp [factorial]
| n + 1 => by
    rw [factorial]
    exact Nat.mul_pos (Nat.succ_pos n) (factorial_pos n)
</syntaxhighlight>
</syntaxhighlight>
== 递归的可视化 ==
递归调用可以通过调用栈可视化。以下是一个阶乘函数的调用流程:
<mermaid>
graph TD
  A["factorial(3)"] --> B["3 * factorial(2)"]
  B --> C["2 * factorial(1)"]
  C --> D["1 * factorial(0)"]
  D --> E["1"]
  E --> C["2 * 1 = 2"]
  C --> B["3 * 2 = 6"]
  B --> A["返回6"]
</mermaid>
== 数学公式支持 ==
递归函数的正确性可以通过数学归纳法证明。例如,阶乘函数的性质可以表示为:
<math>
n! = \begin{cases}
1 & \text{如果 } n = 0, \\
n \times (n-1)! & \text{如果 } n > 0.
\end{cases}
</math>


== 总结 ==
== 总结 ==


* Lean递归函数通过调用自身解决问题,必须满足终止性。
Lean中的递归函数是强大的工具,它:
* 结构递归是Lean中的常见模式,确保函数终止。
* 遵循数学归纳原理
* 模式匹配简化递归函数的定义。
* 需要保证终止性
* 递归广泛应用于列表处理、树遍历和排序算法。
* 可用于计算和证明
* 支持多种递归模式


通过本节的学习,读者应能掌握Lean递归函数的基本原理和实践方法,并能够编写高效的递归程序。
掌握递归是理解Lean和函数式编程的关键步骤。初学者应从结构递归开始,逐步学习更复杂的递归形式。


[[Category:计算机科学]]
[[Category:计算机科学]]
[[Category:Lean]]
[[Category:Lean]]
[[Category:Lean函数式编程]]
[[Category:Lean归纳与递归]]

2025年5月12日 (一) 00:30的最新版本

Lean递归函数[编辑 | 编辑源代码]

Lean递归函数是Lean定理证明器中实现重复计算的核心构造,它允许函数通过调用自身来解决问题。递归在函数式编程和数学归纳证明中具有基础性地位,是处理归纳定义数据类型(如自然数、列表、树等)的必备工具。

基本概念[编辑 | 编辑源代码]

在Lean中,递归函数必须满足终止性(Termination)条件,即所有递归调用必须作用于"更小"的参数,以确保计算最终会停止。这与数学归纳法的原理紧密相关。

递归函数的定义通常包含两个部分:

  • 基例(Base Case):处理最简单的情况,无需递归
  • 递归情况(Recursive Case):将问题分解为更小的子问题

简单示例:阶乘函数[编辑 | 编辑源代码]

以下是用Lean定义的阶乘函数:

def factorial : Nat  Nat
| 0     => 1  -- 基例:0! = 1
| n + 1 => (n + 1) * factorial n  -- 递归情况:(n+1)! = (n+1) * n!

输入输出示例

  • `#eval factorial 5` → 120
  • `#eval factorial 0` → 1

结构递归与良基递归[编辑 | 编辑源代码]

Lean主要支持两种安全的递归形式:

1. 结构递归[编辑 | 编辑源代码]

递归调用只作用于参数的直接子结构,这是最简单的形式:

def list_length {α : Type} : List α  Nat
| []      => 0
| _ :: xs => 1 + list_length xs

2. 良基递归[编辑 | 编辑源代码]

当结构递归不适用时,可以使用well-founded recursion,它依赖一个显式的终止证明:

def ackermann : Nat  Nat  Nat
| 0,   n   => n + 1
| m+1, 0   => ackermann m 1
| m+1, n+1 => ackermann m (ackermann (m + 1) n)
termination_by ackermann m n => (m, n)

递归原理图[编辑 | 编辑源代码]

graph TD A[递归函数调用] -->|参数更小| B[递归调用] B --> C[继续分解] C -->|达到基例| D[返回结果] D --> E[组合结果]

递归与归纳的关系[编辑 | 编辑源代码]

在Lean中,递归和归纳是紧密相关的概念。递归函数的正确性通常需要用归纳法来证明。例如,证明`factorial`函数的正确性:

factorial(n)=k=1nk

实际应用案例[编辑 | 编辑源代码]

案例1:二叉树遍历[编辑 | 编辑源代码]

inductive BinaryTree (α : Type) where
| leaf : BinaryTree α
| node : BinaryTree α  α  BinaryTree α  BinaryTree α

def treeDepth {α : Type} : BinaryTree α  Nat
| .leaf => 0
| .node l _ r => 1 + max (treeDepth l) (treeDepth r)

案例2:快速排序[编辑 | 编辑源代码]

def quicksort : List Int  List Int
| [] => []
| x :: xs =>
    let smaller := quicksort (xs.filter (·  x))
    let larger := quicksort (xs.filter (· > x))
    smaller ++ [x] ++ larger

常见问题与调试[编辑 | 编辑源代码]

1. 非终止递归错误:

  - 确保每次递归调用参数都在减小
  - 使用`termination_by`子句显式指定终止度量

2. 效率问题

  - 避免重复计算(可考虑记忆化)
  - 尾递归优化(Lean会自动优化部分情况)

高级主题[编辑 | 编辑源代码]

互递归[编辑 | 编辑源代码]

多个函数相互调用:

mutual
def even : Nat  Bool
| 0 => true
| n + 1 => odd n

def odd : Nat  Bool
| 0 => false
| n + 1 => even n
end

递归定理证明[编辑 | 编辑源代码]

递归不仅用于计算,也用于证明:

theorem factorial_pos :  n, 0 < factorial n
| 0     => by simp [factorial]
| n + 1 => by
    rw [factorial]
    exact Nat.mul_pos (Nat.succ_pos n) (factorial_pos n)

总结[编辑 | 编辑源代码]

Lean中的递归函数是强大的工具,它:

  • 遵循数学归纳原理
  • 需要保证终止性
  • 可用于计算和证明
  • 支持多种递归模式

掌握递归是理解Lean和函数式编程的关键步骤。初学者应从结构递归开始,逐步学习更复杂的递归形式。