跳到主内容
λLean 学习站
教程 练习场 练习册 战术速查 AI 助手 0 / 7 课
LEAN 4 · 定理证明器入门

把数学证明,
写进代码里。

在 Lean 里证明一个定理,就像运行一段程序——每一步推理都被机器逐行检查。这里是给数学系新生的中文入门课:7 节课,知识点、示例、练习一站到底。

开始第一课 直接进练习场
first-proof.lean
import Mathlib
example : 1 + 1 = 2 := by
  -- 机器检查,一步到位
  norm_num
证明通过 · Proof OK Q.E.D.
机器验证 · 证明对错由内核检查,不靠人眼 数学圈前沿 · AlphaProof 用 Lean 达 IMO 银牌水平(DeepMind 官方) AI4Math 核心 · 北大-九坤 AI4M 指南列为核心工具
本站的知识正确性保障 ① 全部示例代码、练习册与测验考点已通过官方在线编译器(live.lean-lang.org,Lean 4 + Mathlib)真实编译核验(2026-10-03,示例 22/22、练习册 30/30、练习场 4/4 通过);代码自带 import Mathlib,覆盖粘贴即可独立运行; ② 知识点与战术名称对照 Lean 官方文档与 Mathlib 文档核对,每课底部附官方参考链接; ③ 发现任何错误,告诉我出处,我会立即修正并重新交付。

学习进度 · 一目了然

数据存在本机浏览器里,换设备会从零开始。

— / 7
课程完成
学完一节并答完本课小练习即打勾
— / 30
练习册展开
点开过提示与答案的题数
— / 15
测验答对
7 课小测验累计答对题数
继续学习:第 0 课 → 从第一课开始,10 分钟上手。

成就 · 里程碑

边学边解锁,全部存在本机浏览器。

课程地图 · 从零到第一行证明

7 节课,每节都有知识点、可运行的示例和本课小练习。按顺序学,学完一节自动打勾。

学习路线 · 一镜到底

7 节课按顺序递进,每节都建立在上一节之上。

LESSON 0

认识 Lean

定理证明器是什么,跑通第一个证明

LESSON 1

命题即类型

exact 与 rfl:证明就是一个值

LESSON 2

战术工具箱

intro / apply / constructor 三件套

LESSON 3

逻辑联结词

∧ ∨ ¬:构造与使用两个方向

LESSON 4

数学归纳

induction:自然数的递归真相

LESSON 5

计算战术四兄弟

norm_num / ring / omega / simp

LESSON 6

初等数论实战

整除、素数、最大公约数

GRAD

毕业挑战

官方 124 课互动教程,出了新手村

四大工具 · 随取随用

练习场、练习册、战术速查、AI 助手——遇到问题不用退出本站。

练习场

浏览器里跑真 Lean:官方在线编译器已内嵌,4 段示例代码一键复制。

live.lean-lang.org

练习册

30 道手写证明题,从 rfl 到数论,提示与答案全部经官方编译器核验。

入门 · 进阶 · 挑战

战术速查

24 个常用战术一页查完:作用、语法、什么时候用;附常见报错对照表。

查字典 · 报错自救

AI 助手

贴代码/报错,一键生成提问模板复制到豆包,AI 帮你解释、补全、讲概念。

免 API 过渡版

符号速查 · 打字不卡壳

练习场编辑器里输入反斜杠开头的代号、按空格,自动变成符号。点卡片复制代号。

命令速查 · 写证明的骨架

战术负责「怎么证」,命令负责「声明什么」。示例代码全部经官方编译器核验。

import Mathlib

引入标准数学库,norm_num / ring / omega 等战术都在里面。

import Mathlib

example

直接声明并证明一个命题,用完即弃。

example : 1 + 1 = 2 := by norm_num

theorem

给证明起名字,之后可以在别处引用。

theorem my_add_zero (n : Nat) : n + 0 = n := by rfl

lemma

短定理,作用和 theorem 一样。

lemma prime_two_le (p : Nat) (hp : Nat.Prime p) : 2 ≤ p := by exact hp.two_le

def

定义自己的对象、函数、概念。

def double (n : Nat) : Nat := n + n

#check

查任何名字的类型——记不住定理名就靠它。

#check Nat.Prime

#eval

真算一遍表达式,验证你的直觉——右侧 Lean Infoview 信息栏会显示结果(如 5)。

#eval 2 + 3

:= 与 by

:= 把名字绑定到定义;by 之后进入战术证明模式。

example : 2 ∣ 10 := by norm_num
← 课程目录 第 0 课 · 共 7 课 已完成
LESSON 0

认识 Lean:一个会检查证明的程序

本课目标

  • 知道定理证明器是干什么的——一个检查数学证明的程序
  • 认识三件套:import Mathlib、example、by
  • 在练习场跑通第一个证明 1 + 1 = 2

目标:知道定理证明器是什么,跑通你的第一个证明,学会看报错。

01定理证明器是什么

编译器会检查你的代码有没有类型错误;Lean 干的是同一件事,但检查对象是数学证明。你写下一个命题和一个证明,Lean 的内核逐行验证:这个证明到底证没证出这个命题。写对了机器放行,写错了机器报错——不需要人眼复核,也不存在"看起来对"。

这套思路叫形式化验证(formal verification)。近年数学界最热的新闻都跟它有关:陶哲轩团队把多项式 Freiman–Ruzsa 猜想的证明形式化进了 Lean,谷歌 AlphaProof 基于 Lean 在 IMO 2024 达到银牌水平(DeepMind 官方口径)。

02第一个证明:example 与 by

Lean 代码的基本句式:example : 命题 := by 战术。意思是我声明一个命题,然后用一串战术把它证掉。下面这行就是"证明 1+1=2"——战术 norm_num 负责直接计算数值等式。注意第一行 import Mathlib:它引入数学库,norm_num、ring、omega 这些战术都在里面。练习场默认文件本来就有这一行,站内示例也都自带——直接覆盖粘贴就能跑。

示例 · 第一个证明
import Mathlib
example : 1 + 1 = 2 := by
  norm_num

运行后右侧应显示证明通过(No goals to display)。把 2 改成 3 再跑一次,你会看到报错——恭喜,你见到了 Lean 的拒绝。

03两种运行方式

  • 网页版:live.lean-lang.org(本站练习场已内嵌),浏览器直接跑,零安装,适合入门。
  • 本地版:VS Code + elan 工具链,加载 mathlib 后跑得飞快,适合长期深入。

大一阶段用网页版就够了;等你想做自己的数学项目,再装本地版不迟。

04报错是老师,不是敌人

初学 Lean,一半的时间在看报错。报错会精确到哪一行、哪个战术、期望什么得到什么。学会把报错读完整,你的进步会快一倍——读不懂就贴给 AI 助手。

本课小练习

Q1 / 2

0

本课小结

  • 证明交给 Lean 内核逐行检查:写对放行,写错报错
  • norm_num 直接计算数字等式,是第一个偷懒战术

下一步:去练习册做入门 01–02(rfl / exact),然后开始第 1 课。

下一课:命题即类型 →
官方参考:Lean 官方学习中心 · 官方在线编译器
← 课程目录 第 1 课 · 共 7 课 已完成
LESSON 1

命题即类型:exact 与 rfl

本课目标

  • 理解「命题是类型,证明是值」这句话
  • 会用 exact 把已有证明交出去
  • 弄懂为什么 n + 0 = n 能 rfl、0 + n = n 不能

目标:理解"证明是值",学会两个最基础的战术 exact 和 rfl。

01命题是类型,证明是值

Lean 里有一句核心口号:命题是类型,证明是它的元素。就像 Nat 这个类型的元素是自然数 0、1、2……一样,一个命题 P : Prop 的"元素"就是 P 的证明。

所以 h : P 读作"h 是 P 的一个证明"。证明不是玄学,是一个可以传递、组合、存储的对象。

02exact:用现成的证明收尾

当目标是一个命题、而你手里正好有它的证明时,exact h 直接完工。这是最朴素的战术。

示例 · exact
import Mathlib
example (P : Prop) (h : P) : P := by
  exact h

注意括号里的 h : P 是前提:假设 P 成立,我们要证明 P 成立——把 h 原样交出去就行。

试一个失败的例子:把 example : 1 + 1 = 3 := by norm_num 粘进练习场跑一次——右侧会报错。第一次看到失败是好事:说明机器真的在检查,而不是装样子。

03rfl:定义上相等就用它

当等式两边"按定义就是同一个东西"时,rfl 一步到位。比如加法在 Lean 里按第二个参数递归定义,第一条就是 n + 0 = n,所以:

示例 · rfl
import Mathlib
example (n : Nat) : n + 0 = n := by
  rfl

但注意:0 + n = n 就不能用 rfl——0 在递归参数之外,0 + n 无法按定义展开。怎么证它?第 4 课揭晓(答案:归纳)。

本课小练习

Q1 / 3

0

本课小结

  • rfl 只证「两边定义上相同」,需要推理的等式得换战术
  • exact h 把 h 当证明交出去——证明就是值

下一步:练习册入门 03–04(intro / apply),然后开始第 2 课。

← 上一课 下一课:战术工具箱 →
官方参考:《Theorem Proving in Lean 4》「命题与证明」
← 课程目录 第 2 课 · 共 7 课 已完成
LESSON 2

战术工具箱:intro / apply / constructor

本课目标

  • intro:面对 P → Q,先接住前提
  • apply:把目标「退回」成已有前提
  • constructor:拆开 ∧ 目标

目标:掌握三个最高频的战术,能独立证明简单的逻辑命题。

01intro:把前提接进手里

P → Q 的证明,本质是一个函数:给它 P 的证明,它还你 Q 的证明。intro hP 就是"把参数接住"——之后你的手里多了一个假设 hP : P,目标变成 Q。

示例 · intro
import Mathlib
example (P Q : Prop) : P → Q → P := by
  intro hP hQ
  exact hP

两个 → 就连着 intro 两次。最后手里有 hP、hQ 两个假设,目标 P——exact hP 收工。

用函数的话说:intro hP 是「定义这个函数的参数」,exact hP 是「返回参数」。把这个类比刻进脑子里,后面 apply 的逆向用法就顺理成章。

02apply:逆向使用蕴含

目标和 intro 相反:目标已经是 Q,而假设里有 h : P → Q。这时 apply h 把目标"退回"成 P——如果你能证明 P,就顺着 h 得到 Q。

示例 · apply
import Mathlib
example (P Q : Prop) (h : P → Q) (hP : P) : Q := by
  apply h
  exact hP

03constructor:拆开"且"

目标形如 P ∧ Q(P 且 Q)时,constructor 把它拆成两个子目标 P 和 Q,分别证明即可。子目标之间用 · 分行,代码更清晰。

示例 · constructor
import Mathlib
example (P Q : Prop) (hP : P) (hQ : Q) : P ∧ Q := by
  constructor
  · exact hP
  · exact hQ

本课小练习

Q1 / 2

0

本课小结

  • 蕴含就是函数:intro 接参数,exact 返回结果
  • apply 倒着拆目标,constructor 拆合取

下一步:练习册入门 05–08,然后开始第 3 课(逻辑联结词)。

← 上一课 下一课:逻辑联结词 →
官方参考:《Theorem Proving in Lean 4》「命题与证明」
← 课程目录 第 3 课 · 共 7 课 已完成
LESSON 3

逻辑联结词:∧ ∨ ¬

本课目标

  • 构造 ∧ 用 constructor,使用用 h.1 / h.2
  • 构造 ∨ 用 left / right,使用用 cases
  • 记住 ¬P 的本质是 P → False

目标:会用三个逻辑联结词的"构造"和"使用"两种方向。

01合取 ∧:构造用 constructor,使用用 h.1 / h.2

要证明 P ∧ Q:constructor 拆成两个子目标。反过来,手里有 h : P ∧ Q 时,h.1 是 P 的证明,h.2 是 Q 的证明——像取元组的第一个、第二个分量。

示例 · ∧ 的交换
import Mathlib
example (P Q : Prop) : P ∧ Q → Q ∧ P := by
  intro h
  constructor
  · exact h.2
  · exact h.1

02析取 ∨:构造用 left / right

要证明 P ∨ Q,只需证明其中一边成立:left 把目标变成 P,right 把目标变成 Q。

示例 · ∨ 的左分支
import Mathlib
example (P Q : Prop) : P → P ∨ Q := by
  intro hP
  left
  exact hP

03析取 ∨ 的使用:cases 分情况

手里有 h : P ∨ Q 时,用 cases h with | inl hp => ... | inr hq => ... 分成两支:inl 分支假设 P 成立(得到 hp : P),inr 分支假设 Q 成立(得到 hq : Q)。两支都证完,整个目标才算完。

示例 · cases
import Mathlib
example (P Q : Prop) (h : P ∨ Q) : Q ∨ P := by
  cases h with
  | inl hp => right; exact hp
  | inr hq => left; exact hq

注意:构造 ∨ 用 left / right,使用 ∨ 用 cases——这是「两个方向」的又一例。

04否定 ¬:本质是"导致矛盾"

在 Lean 里,¬ P 的定义就是 P → False:P 推出矛盾。所以处理否定 = 处理蕴含:要证 ¬ P 就 intro;手里有 hn : ¬ P 就把 P 的证明喂给它。

示例 · 双重否定
import Mathlib
example (P : Prop) : P → ¬ ¬ P := by
  intro hP hnn
  exact hnn hP

(¬ ¬ P 就是 (P → False) → False,所以 intro 两次。)

本课小练习

Q1 / 2

0

本课小结

  • 逻辑词分「构造」和「使用」两个方向,战术各司其职
  • cases 是析取的关键:分两支逐个击破

下一步:练习册进阶 13–15(cases / 排中律 / 反证),然后开始第 4 课。

← 上一课 下一课:数学归纳 →
官方参考:《Theorem Proving in Lean 4》「命题与证明」
← 课程目录 第 4 课 · 共 7 课 已完成
LESSON 4

数学归纳:自然数的真相

本课目标

  • 理解自然数归纳法原理:基例 + 递推
  • 会写 induction n with 的两个分支
  • 会用归纳假设 ih 收尾

目标:理解归纳原理,亲手证明 0 + n = n。

01自然数是怎么定义的

自然数只有两种:0,或某个数的后继 succ n(也就是 n+1)。加法按第二个参数递归定义,两条规则:

  • n + 0 = n(定义第一条,直接成立)
  • n + succ m = succ (n + m)(先算 n+m 再加一)

这就是第 1 课里 n + 0 = n 能用 rfl 的原因——它是定义本身;而 0 + n = n 不在定义里,必须用归纳证明。

02归纳原理:两步走

要证明"对所有 n,P(n) 成立",只需两步:

  • 基础步(base):证明 P(0);
  • 归纳步(step):假设 P(n) 成立(这叫归纳假设 ih),证明 P(succ n) 也成立。

因为每个自然数要么是 0、要么是某个数的后继,这两步就覆盖了全部自然数。Lean 的 induction n with | zero => ... | succ n ih => ... 精确对应这两步。

03实战:0 + n = n

示例 · 归纳证明
import Mathlib
example (n : Nat) : 0 + n = n := by
  induction n with
  | zero => rfl
  | succ n ih => simp [ih]

拆开看:

  • zero 情形:目标 0 + 0 = 0,定义直接成立,rfl。
  • succ n ih 情形:目标 0 + succ n = succ n。按加法定义化简成 succ (0 + n) = succ n,再用归纳假设 ih 把 0 + n 换成 n——simp [ih] 一步做完。

这就是数学归纳在 Lean 里的全部:每一步机器都盯着。想把这个结论存成定理,把 example 换成 theorem 名字 即可——mathlib 里它叫 Nat.zero_add。

小贴士:上面的 simp 叫「化简战术」,会自动尝试一堆常见化简规则;simp [ih] 表示「允许它使用归纳假设 ih 来重写」。第 5 课会正式讲它,现在只要记住:simp 方括号里放的是「允许它使用的假设」。

本课小练习

Q1 / 2

0

本课小结

  • 归纳两步:zero 分支是基础,succ n ih 分支是递推
  • 递推步骤两把常用刷子:simp [ih](化简)或 omega(线性算术 + ih)

下一步:练习册进阶 12 与挑战 25–26(整除 / gcd),然后开始第 5 课。

← 上一课 下一课:计算战术 →
官方参考:Mathlib norm_num · Mathlib ring · Mathlib omega
← 课程目录 第 5 课 · 共 7 课 已完成
LESSON 5

计算战术四兄弟:norm_num / ring / omega / simp

本课目标

  • norm_num:具体数字;ring:多项式;omega:线性不等式
  • rw:用等式重写目标,← 反向
  • simp:自动化简,simp [h] 用上假设

目标:让机器替你算数,学会偷懒的正确姿势。

01norm_num:数字计算、整除、素数

凡是"具体数字"的等式、不等式、整除、素数判定,norm_num 基本都能直接算出来。

示例 · norm_num
import Mathlib
example : 7 * 6 = 42 := by
  norm_num

02ring:多项式等式

含变量、需要展开或合并的多项式等式(交换律、结合律、分配律、平方展开),ring 全包。

示例 · ring
import Mathlib
example (x y : Nat) : (x + y) ^ 2 = x ^ 2 + 2 * x * y + y ^ 2 := by
  ring

这就是你熟悉的完全平方公式——机器替你展开并核对两边。

03omega:线性等式与不等式

涉及加法、减法、乘常数、大小比较的线性问题,omega 能自动推理,还能用上已有的假设。

示例 · omega
import Mathlib
example (a b : Nat) (h : a ≤ b) : a + 1 ≤ b + 1 := by
  omega

04办案顺序

遇到代数目标,按这个顺序试:norm_num → ring → omega → simp。它们解决不了的时候,才需要你亲自动手想思路。

05rw:用等式重写目标

手里有等式 h : a = b,目标里出现 a 时,rw [h] 把目标里所有 a 替换成 b;要反向替换用 rw [← h]。

示例 · rw
import Mathlib
example (a b : Nat) (h : a = b) : a + 1 = b + 1 := by
  rw [h]

rw 是最常用的战术之一——练习册挑战 22 用的就是它。

06simp:自动化简

simp 自动应用一批"化简规则"(如 0 + n = n、n + 0 = n、n * 0 = 0),适合清理"显然但啰嗦"的代数式。

示例 · simp
import Mathlib
example (n : Nat) : 0 + n + n = n + n := by
  simp
示例 · simp [h]
import Mathlib
example (a b : Nat) (h : a = b) : a + 0 = b := by
  simp [h]

区别:simp 只做定义化简,simp [h] 还会用你给的假设 h 替换。数学证明里"加 0、乘 0、0 加"这类小尾巴,都交给它。

本课小练习

Q1 / 2

0

本课小结

  • 代数目标按 norm_num → ring → omega → simp 的顺序试,剩下的才自己证
  • rw [h] 用等式重写,← h 反向,一次可接多个

下一步:练习册挑战 17–24,然后开始第 6 课(初等数论)。

← 上一课 下一课:初等数论实战 →
官方参考:Mathlib Nat.Prime · Mathlib Nat.gcd · Mathlib 整除 dvd_refl
← 课程目录 第 6 课 · 共 7 课 已完成
LESSON 6

初等数论实战:整除与素数

本课目标

  • 整除 ∣ 与现成定理 dvd_refl / dvd_trans
  • 素数 Nat.Prime 与性质 two_le
  • 最大公约数 Nat.gcd 与 gcd_comm

目标:用 mathlib 现成的数论概念做证明,体验"站在巨人的肩膀上"。

01整除 ∣:norm_num 直接算

2 ∣ 10 读作"2 整除 10"。对具体数字,norm_num 会做除法验证;对符号,mathlib 提供了整套整除理论。

示例 · 整除
import Mathlib
example : 2 ∣ 10 := by
  norm_num

02dvd_refl:任何数整除自己

整除的"自反性"——n ∣ n——不需要计算,mathlib 里就有定理 dvd_refl,直接用 exact 引用。

示例 · dvd_refl
import Mathlib
example (n : Nat) : n ∣ n := by
  exact dvd_refl n

这就是用现成定理的姿势:知道名字,exact 引过来。

03素数判定:Nat.Prime

mathlib 里的素数概念是 Nat.Prime。对具体数字,norm_num 内置了素数判定。

示例 · 素数
import Mathlib
example : Nat.Prime 7 := by
  norm_num

04最大公约数:Nat.gcd

示例 · gcd
import Mathlib
example : Nat.gcd 12 18 = 6 := by
  norm_num

接下来怎么走:把 Natural Number Game 完整通关,或用 #check / #find 探索 mathlib 里的数论定理库。学到这里,你已经具备自己查资料推进的能力了。

05自查工具箱:sorry / #check / #eval

  • sorry:临时占位符,告诉 Lean「这一步先跳过」。用它把证明骨架搭起来,再逐个填坑。正式提交前必须全部消掉。
  • #check 名字:查看一个定理/函数的完整类型。例:#check Nat.add_comm。
  • #eval 表达式:把可计算的东西直接算给你看。例:#eval Nat.gcd 12 18 得到 6。

学会这三个,你就从「等人喂答案」升级为「自己查资料」。

本课小练习

Q1 / 2

0

本课小结

  • 数论概念在 Mathlib 里都是现成的:dvd_trans、Nat.gcd_comm、hp.two_le
  • 记不住定理名?#check 在练习场里查类型

下一步:练习册挑战 25–30 收尾,翻一遍战术速查,去 lean4.dev 打毕业挑战。

← 上一课 学完了?去练习场自由练习 →
官方参考:Mathlib Nat.Prime · Mathlib Nat.gcd · Mathlib 整除 dvd_refl
PRACTICE

练习场 · 浏览器里跑真 Lean

下方内嵌官方在线编译器(live.lean-lang.org)。点击加载后即可在浏览器里运行完整 Lean 4 + Mathlib:左边写代码,右边实时显示检查结果。首次加载数学库约需一两分钟,按需加载更省流量。

内嵌官方编译器需要下载较大资源包,按需加载更省流量。

加载不了?直接打开官方编辑器 ↗
如果内嵌窗口没有显示(受网络或浏览器设置影响),点右上角导航「AI 助手」旁的练习场外链,或直接打开 live.lean-lang.org 在新窗口使用。
第一步 · 清空

在左栏编辑器中全选(Ctrl+A),直接粘贴覆盖。示例自带 import Mathlib 行,覆盖后就能跑。

第二步 · 粘贴

点下方示例的「复制」,把代码粘进左栏。

第三步 · 看结果

右栏出现「No goals to display / 证明通过」就是成功;有报错就复制去问 AI 助手。

示例代码 · 从入门到挑战

LEVEL 1 · 数值计算 norm_num 是计算数值等式的主力战术
import Mathlib
example : 1 + 1 = 2 := by
  norm_num
LEVEL 2 · 逻辑与 constructor 把 P ∧ Q 拆成两个子目标
import Mathlib
example (P Q : Prop) (hP : P) (hQ : Q) : P ∧ Q := by
  constructor
  · exact hP
  · exact hQ
LEVEL 3 · 数学归纳 证明「0 + n = n」需要归纳,这是第 4 课的重点
import Mathlib
example (n : Nat) : 0 + n = n := by
  induction n with
  | zero => rfl
  | succ n ih => simp [ih]
CHALLENGE · 加法交换律 omega 战术一键解决线性算术;换成你手写试试
import Mathlib
example (a b : Nat) : a + b = b + a := by
  omega
PRACTICE BOOK

练习册 · 30 道手写证明

从入门写到挑战。每道题先在练习场里自己试,卡住了再看提示,最后对答案。30 道全部经官方在线编译器(live.lean-lang.org,Lean 4 + Mathlib)真实编译核验(2026-10-03),复制答案可直接运行。

抽一道题练手,写不出来就翻提示。

入门 · 热身

入门 01 · rfl n + 0 = n
example (n : Nat) : n + 0 = n := by
  -- 你试试怎么写
提示与答案

提示:n + 0 是加法定义的第一条——第二个参数是 0 时直接返回 n,两边定义上相同。这正是第 1 课 rfl 的用武之地。

import Mathlib
example (n : Nat) : n + 0 = n := by
  rfl
入门 02 · exact 由 h : P → Q 和 hp : P 证 Q
example (P Q : Prop) (h : P → Q) (hp : P) : Q := by
  -- 你试试怎么写
提示与答案

提示:exact 直接把一个已有的证明交出去。h hp 是什么?把 hp 喂给 h(函数应用),就得到 Q 的证明。

import Mathlib
example (P Q : Prop) (h : P → Q) (hp : P) : Q := by
  exact h hp
入门 03 · intro P → Q → P
example (P Q : Prop) : P → Q → P := by
  -- 你试试怎么写
提示与答案

提示:蕴含就是函数:intro 依次接住两个前提 hp 和 hq。证明 P 用不到 hq,直接 exact hp 交回。

import Mathlib
example (P Q : Prop) : P → Q → P := by
  intro hp hq
  exact hp
入门 04 · apply 三段蕴含:R
example (P Q R : Prop) (hPQ : P → Q) (hQR : Q → R) (hp : P) : R := by
  -- 你试试怎么写
提示与答案

提示:目标 R,apply hQR 把它"退回"成 Q,再 apply hPQ 退回成 P,最后 exact hp 收尾。

import Mathlib
example (P Q R : Prop) (hPQ : P → Q) (hQR : Q → R) (hp : P) : R := by
  apply hQR
  apply hPQ
  exact hp
入门 05 · rw 反向 由 b = a 证 a = b
example (a b : Nat) (h : b = a) : a = b := by
  -- 你试试怎么写
提示与答案

提示:rw 不在乎等式写在哪个方向——rw [h] 把目标里所有 b 替换成 a,目标立刻变成 a = a。

import Mathlib
example (a b : Nat) (h : b = a) : a = b := by
  rw [h]
入门 06 · omega 加法的结合律
example (a b c : Nat) : a + (b + c) = (a + b) + c := by
  -- 你试试怎么写
提示与答案

提示:加法结合律对 omega 来说只是"括号怎么摆不影响"的线性事实,一行收工。

import Mathlib
example (a b c : Nat) : a + (b + c) = (a + b) + c := by
  omega
入门 07 · constructor 嵌套 证 P ∧ Q ∧ R
example (P Q R : Prop) (hP : P) (hQ : Q) (hR : R) : P ∧ Q ∧ R := by
  -- 你试试怎么写
提示与答案

提示:∧ 右结合,P ∧ Q ∧ R 是 P ∧ (Q ∧ R)。constructor 一次拆一层,第二层再 constructor 一次。点号 · 表示按顺序处理子目标。

import Mathlib
example (P Q R : Prop) (hP : P) (hQ : Q) (hR : R) : P ∧ Q ∧ R := by
  constructor
  · exact hP
  · constructor
    · exact hQ
    · exact hR
入门 08 · intro + exact P → Q 原样还给你
example (P Q : Prop) (h : P → Q) : P → Q := by
  -- 你试试怎么写
提示与答案

提示:intro 接住前提 P 的证明 hp,然后 exact h hp——把 hp 当参数交给 h,这就是"原样返回"。

import Mathlib
example (P Q : Prop) (h : P → Q) : P → Q := by
  intro hp
  exact h hp

进阶 · 组合逻辑与归纳

进阶 09 · ∧ 由 h : P ∧ Q 证 Q ∧ P
example (P Q : Prop) (h : P ∧ Q) : Q ∧ P := by
  -- 你试试怎么写
提示与答案

提示:constructor 把 Q ∧ P 拆成两个子目标;h.1 是 P 的证明,h.2 是 Q 的证明,对调着用。

import Mathlib
example (P Q : Prop) (h : P ∧ Q) : Q ∧ P := by
  constructor
  · exact h.2
  · exact h.1
进阶 10 · ∨ 由 P 证 P ∨ Q
example (P Q : Prop) (h : P) : P ∨ Q := by
  -- 你试试怎么写
提示与答案

提示:析取只需一边成立:left 选择证明左边 P,right 则选右边。手头有 h : P,选 left。

import Mathlib
example (P Q : Prop) (h : P) : P ∨ Q := by
  left
  exact h
进阶 11 · ¬ 由 P 证 ¬ ¬ P
example (P : Prop) (h : P) : ¬ ¬ P := by
  -- 你试试怎么写
提示与答案

提示:¬P 的本质就是 P → False。intro 接住"¬P 的证明" hn,再把 h 喂给它得到矛盾。

import Mathlib
example (P : Prop) (h : P) : ¬ ¬ P := by
  intro hn
  exact hn h
进阶 12 · induction n + n = 2 * n
example (n : Nat) : n + n = 2 * n := by
  -- 你试试怎么写
提示与答案

提示:对 n 归纳:zero 情形 rfl;succ 情形展开后是线性等式,omega 会用上归纳假设 ih 收尾。

import Mathlib
example (n : Nat) : n + n = 2 * n := by
  induction n with
  | zero => rfl
  | succ n ih => omega
进阶 13 · cases P ∨ Q 证 Q ∨ P
example (P Q : Prop) (h : P ∨ Q) : Q ∨ P := by
  -- 你试试怎么写
提示与答案

提示:手里有析取就分情况:cases h 拆成 inl(P 成立)和 inr(Q 成立)两支,各自选 right/left 收尾。战术见「战术速查」。

import Mathlib
example (P Q : Prop) (h : P ∨ Q) : Q ∨ P := by
  cases h with
  | inl hp => right; exact hp
  | inr hq => left; exact hq
进阶 14 · 取反 证 ¬ (¬ P ∧ P)
example (P : Prop) : ¬ (¬ P ∧ P) := by
  -- 你试试怎么写
提示与答案

提示:intro 接住假设 h : ¬P ∧ P。h.1 是"P 的否定"(即 P → False),把 h.2(P 的证明)喂给它,就得到 False。

import Mathlib
example (P : Prop) : ¬ (¬ P ∧ P) := by
  intro h
  exact h.1 h.2
进阶 15 · by_cases 排中律:P ∨ ¬ P
example (P : Prop) : P ∨ ¬ P := by
  -- 你试试怎么写
提示与答案

提示:by_cases h : P 按"P 成立与否"分两支:成立就 left,不成立(h 就是 ¬P)就 right。这正是排中律的用法。

import Mathlib
example (P : Prop) : P ∨ ¬ P := by
  by_cases h : P
  · left
    exact h
  · right
    exact h
进阶 16 · 组合 (P → Q) → (Q → R) → P → R
example (P Q R : Prop) : (P → Q) → (Q → R) → P → R := by
  -- 你试试怎么写
提示与答案

提示:三个前提全部 intro 接住,然后像套函数一样组合:hQR (hPQ hP)。和挑战 15 是同一条路,只是假设写法不同。

import Mathlib
example (P Q R : Prop) : (P → Q) → (Q → R) → P → R := by
  intro hPQ hQR hP
  exact hQR (hPQ hP)

挑战 · 计算与数论

挑战 17 · ring 完全平方公式
example (a b : Nat) : (a + b)^2 = a^2 + 2 * a * b + b^2 := by
  -- 你试试怎么写
提示与答案

提示:含变量的多项式等式交给 ring——它负责展开、合并、消项。

import Mathlib
example (a b : Nat) : (a + b)^2 = a^2 + 2 * a * b + b^2 := by
  ring
挑战 18 · omega ≤ 的传递性
example (a b c : Nat) (h1 : a ≤ b) (h2 : b ≤ c) : a ≤ c := by
  -- 你试试怎么写
提示与答案

提示:把两条不等式假设都放进上下文,omega 对线性不等式做自动推理。

import Mathlib
example (a b c : Nat) (h1 : a ≤ b) (h2 : b ≤ c) : a ≤ c := by
  omega
挑战 19 · norm_num 2^10 = 1024
example : 2^10 = 1024 := by
  -- 你试试怎么写
提示与答案

提示:具体数字的算术统一交给 norm_num。

import Mathlib
example : 2^10 = 1024 := by
  norm_num
挑战 20 · gcd Nat.gcd 12 18 = 6
example : Nat.gcd 12 18 = 6 := by
  -- 你试试怎么写
提示与答案

提示:最大公约数对具体数字是可计算的,norm_num 直接算出 6。

import Mathlib
example : Nat.gcd 12 18 = 6 := by
  norm_num
挑战 21 · dvd_refl n ∣ n
example (n : Nat) : n ∣ n := by
  -- 你试试怎么写
提示与答案

提示:整除的自反性在 Mathlib 里是现成定理 dvd_refl,直接 exact。

import Mathlib
example (n : Nat) : n ∣ n := by
  exact dvd_refl n
挑战 22 · rw a = b 推出 a+1 = b+1
example (a b : Nat) (h : a = b) : a + 1 = b + 1 := by
  -- 你试试怎么写
提示与答案

提示:rw [h] 用假设 h : a = b 把左边的 a 重写为 b,两边定义上相同后自动收尾。

import Mathlib
example (a b : Nat) (h : a = b) : a + 1 = b + 1 := by
  rw [h]
挑战 23 · De Morgan ¬(P ∨ Q) → ¬ P ∧ ¬ Q
example (P Q : Prop) : ¬ (P ∨ Q) → ¬ P ∧ ¬ Q := by
  -- 你试试怎么写
提示与答案

提示:目标是蕴含:intro h 接住 ¬ (P ∨ Q),再 constructor 拆成 ¬ P 和 ¬ Q 两个子目标;每个子目标再 intro 拿到假设,最后把 Or.inl hp(或 Or.inr hq)喂给 h 制造矛盾。体会「¬P 就是 P → False」。

import Mathlib
example (P Q : Prop) : ¬ (P ∨ Q) → ¬ P ∧ ¬ Q := by
  intro h
  constructor
  · intro hp; exact h (Or.inl hp)
  · intro hq; exact h (Or.inr hq)
挑战 24 · rw 链 等式的传递性
example (a b c : Nat) (h1 : a = b) (h2 : b = c) : a = c := by
  -- 你试试怎么写
提示与答案

提示:rw 可以一次接多个等式:rw [h1, h2] 先用 h1 把 a 换成 b,再用 h2 把 b 换成 c,目标自动收尾。

import Mathlib
example (a b c : Nat) (h1 : a = b) (h2 : b = c) : a = c := by
  rw [h1, h2]
挑战 25 · dvd_trans 整除的传递性
example (a b c : Nat) (h1 : a ∣ b) (h2 : b ∣ c) : a ∣ c := by
  -- 你试试怎么写
提示与答案

提示:若 a 整除 b、b 整除 c,则 a 整除 c——Mathlib 已备好定理 dvd_trans,像挑战 21 的 dvd_refl 一样直接 exact。

import Mathlib
example (a b c : Nat) (h1 : a ∣ b) (h2 : b ∣ c) : a ∣ c := by
  exact dvd_trans h1 h2
挑战 26 · gcd_comm gcd 的交换律
example (a b : Nat) : Nat.gcd a b = Nat.gcd b a := by
  -- 你试试怎么写
提示与答案

提示:gcd 的交换律在 Mathlib 里叫 Nat.gcd_comm。学会"搜定理名":记不住就 #check Nat.gcd_comm 在练习场里试。

import Mathlib
example (a b : Nat) : Nat.gcd a b = Nat.gcd b a := by
  exact Nat.gcd_comm a b
挑战 27 · ring 分配律 n*n + n = n*(n+1)
example (n : Nat) : n * n + n = n * (n + 1) := by
  -- 你试试怎么写
提示与答案

提示:把右边的括号展开:n*(n+1) = n*n + n。这是分配律,ring 自动处理多项式。

import Mathlib
example (n : Nat) : n * n + n = n * (n + 1) := by
  ring
挑战 28 · rw 双向 由两个等式证传递
example (a b c : Nat) (h1 : a = b) (h2 : c = b) : a = c := by
  -- 你试试怎么写
提示与答案

提示:两条等式都指向 b,方向不同。rw [h1] 先把 a 换成 b;再用 ← h2 反向把 b 换成 c,收尾。← 是 Lean 的"反向重写"符号。

import Mathlib
example (a b c : Nat) (h1 : a = b) (h2 : c = b) : a = c := by
  rw [h1, ← h2]
挑战 29 · by_contra 反证法:¬P → Q 且 ¬Q 推出 P
example (P Q : Prop) (h : ¬ P → Q) (hnq : ¬ Q) : P := by
  -- 你试试怎么写
提示与答案

提示:反证法:假设 ¬P(by_contra hnP),推出矛盾。h hnP 给出 Q,与 ¬Q 的 hnq 冲突,矛盾即证。

import Mathlib
example (P Q : Prop) (h : ¬ P → Q) (hnq : ¬ Q) : P := by
  by_contra hnP
  exact hnq (h hnP)
挑战 30 · 素数性质 素数至少是 2
example (p : Nat) (hp : Nat.Prime p) : 2 ≤ p := by
  -- 你试试怎么写
提示与答案

提示:Nat.Prime 的定义自带一条性质 two_le(素数 ≥ 2)。用点号取字段:hp.two_le。这是从定义里"挖定理"的第一课。

import Mathlib
example (p : Nat) (hp : Nat.Prime p) : 2 ≤ p := by
  exact hp.two_le
← 去练习场写题 卡住了?去问 AI 助手 →
CHEATSHEET

战术速查 · 24 个常用招

写证明卡住时先来这里翻。每个战术:什么时候用、怎么用。下面是语法示意,想跑真实例子去对应课程或练习册。

一步收尾

rfl 定义相同

目标两边定义上完全一样时,直接收尾。

example (n : Nat) : n + 0 = n := by rfl
exact 交出证明

你手上正好有一个目标类型的证明,直接交出去。

exact h -- h 的类型就是目标 exact h hp -- 把 hp 喂给 h,得到目标
assumption 翻上下文

目标恰好是上下文里的某个假设,让 Lean 自己找。

example (P : Prop) (h : P) : P := by assumption
by 进入战术模式

证明写在一个 by 块里,后面的行都是战术。

example : 1 + 1 = 2 := by norm_num

处理蕴含 →

intro 接住前提

目标是 P → Q 时,把 P 的证明当作参数接进来,继续证 Q。

example (P Q : Prop) : P → Q → P := by intro hp hq exact hp
apply 倒着拆

目标恰好是某个函数/定理的结论,把它"退回"成前提。

apply hQR -- 目标 R,退成 Q apply hPQ -- 目标 Q,退成 P

逻辑构造与分解

constructor 拆 ∧

目标是 P ∧ Q,拆成两个子目标分别证。

constructor · exact hP · exact hQ
left / right 选一边

目标是 P ∨ Q,只要证一边成立。

left -- 改证左边 P right -- 改证右边 Q
cases 拆 ∨

手里有 P ∨ Q,按两边分别讨论。

cases h with | inl hp => ... | inr hq => ...
rcases 拆且命名

cases 的加强版:同时拆多个假设并自动命名。

rcases h with ⟨hp, hq⟩ -- 拆 h : P ∧ Q
by_cases 排中分支

对命题 P 分"成立/不成立"两支(依赖排中律)。

by_cases h : P · left; exact h · right; exact h
by_contra 经典反证法

假设结论的否定,推出矛盾。依赖经典逻辑(排中律),在直觉主义构造里不成立。

by_contra h -- 得到 h : ¬ 目标 -- 推出 False 即可
· 点号 按序处理子目标

有多个子目标时,用点号逐个击破,顺序清晰。

constructor · exact hP · exact hQ

归纳与化简

induction 数学归纳法

对自然数 n 归纳:基础情形 + 归纳步骤(ih 是归纳假设)。

induction n with | zero => rfl | succ n ih => omega
have 建立中间结论

在证明中途先证一个小结论,给它起名字,后面引用。

have h1 : a + 0 = a := by rfl -- 之后可用 h1
suffices 只需证

把目标弱化成更简单的中间结论,先证它,再用它推出原目标。

suffices h : a ≤ b by omega -- 原目标归约为证 a ≤ b
simp 自动化简

应用化简规则(0+n=n 之类),也能用给定假设。

simp -- 只用化简规则 simp [h] -- 顺便用假设 h 替换
rw 等式重写

用等式把目标里的表达式换掉,← 是反向。

rw [h] -- 目标里所有 a → b(h : a = b) rw [h1, ← h2] -- 依次重写,可接多个
calc 链式推导

像草稿纸一样逐步推导:目标 = 中间式 = … = 结论。

calc a + 1 = b + 1 := by rw [h] _ = c + 1 := by rw [h2]

计算自动化

norm_num 具体数字

具体数字的四则运算、幂、gcd、素数判断,一步算清。

example : 2^10 = 1024 := by norm_num
ring 多项式等式

交换律、结合律、分配律、平方展开,含变量也能算。

example (x y : Nat) : (x + y)^2 = x^2 + 2*x*y + y^2 := by ring
omega 线性算术

加、减、乘常数、大小比较的线性命题,能用上下文假设。

example (a b c : Nat) (h1 : a ≤ b) (h2 : b ≤ c) : a ≤ c := by omega

查看与排错

#check 查类型

查一个名字的类型/声明,忘了定理长什么样用它。

#check Nat.gcd_comm -- Nat.gcd a b = Nat.gcd b a
#eval 算值

让 Lean 真的算一个表达式并显示结果。

#eval 17 % 5 -- 2 #eval Nat.gcd 12 18 -- 6
sorry 临时占位

先跳过这一步(会警告)。只用来占位置,交作业前必须补上。

example : P := by sorry
exact? 搜现成引理

让 Lean 在当前目标上搜索一个刚好能闭合它的现成定理,找到后在信息栏给出建议,直接写进代码即可。

by exact? -- 若目标能用现成引理直接证,会给出提示

常见报错 · 什么意思怎么办

Lean 4 常见报错对照表:左列为报错文本,右列为含义与对策
报错含义与对策
unknown identifier 'foo'Lean 不认识这个名字:拼写错了,或没 import Mathlib,或这个名字根本不存在。用 #check 验证。
type mismatch你给的东西类型和目标对不上。把两边的类型都写出来对一遍,常见是把 a = b 错用成 b = a 方向。
unsolved goals战术用完了但目标没证完。看目标区还剩什么,补最后一步(rfl / exact / assumption)。
tactic 'rfl' failed两边定义上不一样。别硬 rfl,换 simp / norm_num / ring / omega,或先用 rw 化简。
failed to synthesizeLean 找不到要的东西,最常见是忘了 import Mathlib;要的定理可能要用 #check 先搜出来再 exact。
← 回练习册开练 还看不懂?去问 AI 助手 →
AI ASSISTANT

AI 助手 · 报错看不懂?贴进来

把 Lean 代码或报错粘到下面,点「生成提问并复制」,再到豆包粘贴发送。AI 会帮你解释报错、补全证明、讲清概念。当前为免配置过渡版:不接 API、不花钱,复制即用。

把内容贴进来

支持粘贴代码、报错信息,或两者一起。不确定问什么,先点下方场景按钮。

三种问法,照着来

① 报错看不懂

贴上代码和报错,问:"解释哪里错了,给出修改后的完整代码"。

② 证明写不出来

贴上题目和已写的部分,问:"帮我补全证明,给完整可运行的 Lean 代码"。

③ 概念没听懂

贴上一段代码,问:"用大一新生能懂的方式讲这段代码用到的 Lean 概念"。

Lean 学习站 · 面向数学系新生的 Lean 4 中文入门课程。本站为学习作品,示例基于官方在线编译器 live.lean-lang.org 运行;知识点已对照 Lean 官方文档与 Mathlib 核对,每课附官方参考。
示例与测验已于 2026-10-03 经官方在线编译器(Lean v4.35-rc3 + Mathlib)真实编译核验通过:示例 22/22、练习册 30/30、练习场 4/4。
官方在线编译器 Lean 官方学习中心 lean4.dev 互动课程