空间中的有向箭头, 列表 计算: 向量相加,向量数乘 # 向量的线性组合: 向量的数乘之和 a * $\vec{v}$ + b * $\vec{w}$ (a b 是可以任意变化的,代表对向量的伸缩) 张成的空间:所有表示为给定定向量线性组合的向量集合e.g. 一个平面就是两个(任意维)线性无关向量张成的空间;三个三维线性无关向量张成的空间三维空间 Linearly dependent: 一组向量中至少有一个是多余的,没有对张成的空间做出任何贡献,当有多个向量移除一个向量不会减少张成的空间,就称它们线性相关(其中一个向量可以被其他所有向量的线性组合表示出来) Linearly independent:一组向量都增加了张成的空间 the only solution to $a * \vec{v} + b * \vec{w} + c * \vec{u} = \vec{0} $ is a = b = c = 0
The basis of a vector space is a set of linearly independent vectors that span the full space.基就是张成该空间的线性无关向量的集合
linear transformation(变换后直线依旧是直线,原点固定,看作保持网格线平行并且等距分布)
term 包含很多信息,可以从中推断出信息 Lean 是基于依赖类型理论Calculus of Constructions,with a countable hierarchy of non-cumulative universes and inductive types,有编译器(生成可执行二进制文件)和interactive interpreter
Simple Type Theory
Lean natural number is a 任意精度的无符号整数
1 2 3 4 5 6 7 8
/- def 声明new constant symbols #check,#eval 辅助性命令一般#开头 类型理论:a -> b 表示从a到b的函数类型 描述f是关于x的函数 f x(e.g. Nat.succ 2) arrows associate to the right (Nat -> Nat -> Nat 是等同于Nat -> (Nat -> Nat)) 函数的partially apply: Nat.add 3等同于Nat.add 3 n 返回了一个函数等待第二个参数n -/
-- Lean的底层基础无限类型层次,Type 0是小范围的类型,Type 1是更大的类型,包含Type 0作为一个元素,以此类推 #check Type -- Type 1 #check Type 1 -- Type 2 -- Lean依赖类型理论扩展的方式 -- 1. 类型本身,它们的类型是Type
---------------------------------------------------------- -- 定义polymorphic constants(使用universe或者在定义时提供universe parameters) universe u def F (\a : Type u) : Type u := Prod \a \a ------------------------------------------ def G.{v} (\a : Type v) :Type v:= Prod \a \a
Function Abstraction and Evaluation
creating a function from another expression is a process known as lambda abstraction
1 2 3 4 5 6 7
-- fun 也可以用 λ 代替 #check (fun x => x+1) -- Nat -> Nat,此处leave off type notation省略了类型注释 /- fun (x : type) => t 其中()可以省略 对于x:\a 变量,可以构造表达式t:\b λ (x: \a) => t 表示函数from \a to \b 将x 映射到t 其中x 是bound variable,占位符,whose scope does not extend beyond the expression t -/
alpha equivalent : expression that are the same up to renaming of bound variables are called alpha equivalent, and considered as “the same”.
definitionally equal: two terms that reduce to the same value are called definitionally equal
namespaces organize data and sections declare variables for insertion in definitions;sections are also useful for delimiting限制 the scope of commands such as “set_option” and “open”;对于variable 和 open命令,随着namespace关闭也失效
1 2 3 4 5 6 7 8 9
namespace foo namespace bar end bar end foo
open foo --可以在当前使用非嵌套foo的短名
namespace foo end foo
What makes dependent type dependent
看不懂举的例子是什么意思???????不明白传值的时候哪个对应哪个!!! type can depend on parameters dependent function type(dependent arrow type) : (a:\a)->\b 其中\b 的值依赖于a (e.g. (a:\a) -> \b a)
Dependent products are also called sigma types, and you can also write them as Σ a : α, β a. You can use ⟨a, b⟩ or Sigma.mk a b to create a dependent pair. The ⟨ and ⟩ characters may be typed with \langle and \rangle or < and >, respectively.
implicit arguments
1 2 3 4 5 6 7 8 9 10 11 12 13
-- sorry produces a proof of anything or provides an object of any data type at all,但是不能用来证明False,会报错 -- _ placeholder代表implicit argument,表示自动填充 -- 可以从传入的某个参数确定一些类型参数,此时类型参数可以用_代替
-- 也可以在定义时,用 {}将可以推断的参数括起来,在传入时就无需显示指明
-- (e : T) to specify the type T of an expression e ,这样写告诉Lean的elaborator 在解决隐式参数时使用T作为e的Type
-- @List -- 描述相同的方法,不过所用的参数都变成了explicit
-- Numerals are overloaded in Lean #check 2 -- Nat #check (2 : Int) -- Int
Lean 的实例化隐式参数(instantiating implicit arguments)可以用来infer function types , predicates , proofs elaboration : the process of instantiating these “holes” or “placeholders”
Propositions and Proofs
Propositions as Types
proposition represents a sort of data type. if proposition p is true then the type associated with p is inhabited.constructing t : p tell us p is indeed true.
1 2 3 4 5 6
-- Prop is Sort 0 -- Type u is Sort (u+1) -- if p q: Prop, then p \r q :Prop
-- if p : Prop , t1 t2:p 则 t1 t2是相等的 -- (fun x => t) s and t[s/x] as definitionally equal这个后者直接写这样会报错,所以不理解
Working with Propositions as Types
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/- -- 'theorem' command is really a version of 'def' command 对于类型检查器没有任何区别 Lean tags proof as irreducible 不可归约?, which serves as a hint to the parser(elaborator)that is generally no need to unfold them when processing a file.为何不需要展开? -- Lean 并行处理证明和进程,因为proof的 irrelevance,一个proof 的正确性无需另一个定理的细节 -/
-- #print theorem_name -- show you the proof of a theorem
-- 显示指明临时假设的类型 #show variable {p : Prop} variable {q : Prop} theorem t1 : p \r q \r p := fun hp : p => fun hq : q => show p from hp -- 原先只写 hp 就可以
axiom hp : p -- 等价于声明 p is true, as witnessed by hp
the axiom declaration postulates假设 the existence of an element of the given type and may compromise影响 logical consistency.
Propositional Logic
The order of operations is as follows: not > and > or > \imp > \iff其中\imp(\r)右结合 连接词的定义在库 Prelude.core中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
-- example command states a theorem without naming it or storing it in the permanent context,只是检查了term的类型
-- e.bar 是 Foo.bar.e 的缩写,不需要打开一个namespase variable (ls : List Nat) #check ls.length #check List.length ls
-- for auto construction \< \> example (h : p \and q): q \and p \and q := \< h.right, h \> -- 或者 \< h.right , \< h.left, h.right\>\> -- 或者 And.intro h.right h -- h.left = And.left -- Or.elim (h :p \or q) (left :p \r c) (right :q \r c) :c简写 h.elim (left) (right)
-- or 有两个构造器constructors,so 不能匿名构造,但仍然可以简写 -- 什么是constructors???
1 2 3
-- ex falso sequitur quodlibet False \r c从错误中能推出任意事实 example (hp : p) (hnp : \neg p) : q := Flase.elim (hnp hp) -- absurd hp hnp
Introfucing Auxilisry Subgoals
1 2 3 4 5 6 7 8 9
-- have (h : p) := s;t 和 (fun (h : p) =>t) s 是一样的;s是p的证明,t是(h:p)期望假设的证明
/- suffices to show: resoning backwards from a goal -/ example (h : p∧ q) : q ∧ p := have hp : p := h.left suffices hq:q from And.intro hq hp -- leave us with two goals,1 . by proving the original goal of q \and p with additional hypothesis hq : q, 2. have to show q show q from And.right h
Classical Logic
namespace Classical 中是典型逻辑,添加了excluded middle
1 2 3 4 5 6 7 8
open Classical variable (p : Prop) #check em p -- p ∨ ¬ p
-- 反证法形式化 #check byContradiction -- (\neg p -> False) -> p -- 通过案例 #check byCases -- (p -> q) (\neg p -> q) -> q
Quantifiers And Equality
The Universal Quantifier
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
-- x : \a ,p x 表示 p that holds of x -- 任意 x : \a, p x
/- introduction:
Given a proof of p x, in a context where x : α is arbitrary, we obtain a proof ∀ x : α, p x.
Given a term t of type β x, in a context where x : α is arbitrary, we have (fun x : α => t) : (x : α) → β x.
elimination:
Given a proof ∀ x : α, p x and any term t : α, we obtain a proof of p t.
Given a term s : (x : α) → β x and any term t : α, we have s t : β t. -/
the expressions which differ up to renaming of bound variables are considered to be equivalent
1 2 3 4 5
-- 可以都用x来表示 example (α : Type)(p q : α → Prop): (∀ x : α , p x ∧ q x) → ∀ x : α , p x := fun (h :∀ x : α , p x ∧ q x) => (fun (z : α ) => -- 在此处重命名了变量 show p z from (h z).left)
It is the typing rule for dependent arrow types, and the universal quantifier in particular, that distinguished Prop from other types.依赖箭头的类型是箭头两边的最大类型,如果箭头右边是 Sort 0类型,则依赖箭头类型就是Sort 0
Suppose we have α : Sort i and β : Sort j, where the expression β may depend on a variable x : α. Then (x : α) → β is an element of Sort (imax i j), where imax i j is the maximum of i and j if j is not 0, and 0 otherwise. Prop as the type of proposition rather than data,and it is what makes Prop impredicative 类型的幂: Notice that if α is any type, we can form the type α → Prop of all predicates on α (the “power type of α”)
/- calc <expr>_0 'op_1' <expr>_1 ':=' <proof>_1 --expr<>_0可以单独一行,下面用'_' /- <expr>_0 '_' 'op_1' <expr>_1 ':=' <proof>_1 -/ '_' 'op_2' <expr>_2 ':=' <proof>_2 ... '_' 'op_n' <expr>_n ':=' <proof>_n -- each <proof>_i is a proof for <expr>_{i-1} op_i <expr>_i -/
1 2 3 4 5 6 7 8 9
/- `rw` tactics: 重写目标
use a given equality (which can be a hypothesis, a theorem name, or a complex term) to "rewrite" the goal. If doing so reduces the goal to an identity `t = t` ,the tatic applies reflexivity to prove it
`simp` tactics:更机智的重写目标,自动选择和重复的重写
rewrites the goal by applying the given identities repeatedly, in any order, anywhere they are applicable in a term. it also uses other rules that have been previously declared to the system, and applies commutativity wisely to avoid looping. -/
1
--`存在Exists.intro` : Existential introduction. If a : α and h : p a, then ⟨a, h⟩ is a proof that ∃ x : α, p x
The Existential Quantifier
the library includes both an introduction rule and an elimination rule 对于introduction rule : to prove Exists x : \a, p x, need suitable term t and a proof of p t 只需要显示提供任意一个可取的值,以及该值下成立的假设即可,
1 2 3 4 5 6 7 8 9
-- set_option pp.explicit true 后使用#print 可以display implicit argument /- -- Exists.intro.{u} {α : Sort u} {p : α → Prop} (w : α) (h : p w) : Exists p -- implicit argument {p : α → Prop}意味着Lean能自动推出原命题,但是根据提供的值可以有很多假设:Lean use the context to infer which one is appropriate example : ∃ x : Nat, x > 0 := have h : 1 > 0 := Nat.zero_lt_succ 0 Exists.intro 1 h -- ⟨1, h⟩ -/
/- -- Exists.elim.{u} {α : Sort u} {p : α → Prop} {b : Prop}(h₁ : ∃ x, p x) (h₂ : ∀ (a : α), p a → b) : b -/ variable (α : Type) (p q : α → Prop)
example (h : ∃ x, p x ∧ q x) : ∃ x, q x ∧ p x := Exists.elim h (fun w => fun hw : p w ∧ q w => show ∃ x, q x ∧ p x from ⟨w, hw.right, hw.left⟩) /-`match .\a -> prop.. with | ...` :拆分组件(组件自命名,可拆的数目多种多样自助) the `match` statement destructs the exstential assertion into the components `w` and `hw`, which can be then used in the body of the statement to prove the proposition. -/ variable (α : Type) (p q : α → Prop)
example (h : ∃ x, p x ∧ q x) : ∃ x, q x ∧ p x := match h with | ⟨w, hw⟩ => ⟨w, hw.right, hw.left⟩
-- variable (α : Type) (p q : α → Prop) example (h : ∃ x, p x ∧ q x) : ∃ x, q x ∧ p x := match h with | ⟨w, hpw, hqw⟩ => ⟨w, hqw, hpw⟩ -- | ...=> ... /- `let` : pattern-matching ,similar to `match h with | <> => <>`-/ example {α : Type}{p q : α → Prop} (h : ∃ x ,p x ∧ q x ): ∃ x , q x ∧ p x:= let ⟨ w, hpw, hqw⟩:= h ⟨ w,hqw,hpw⟩
更多证明语言
this 代表上一个have匿名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
variable (f : Nat → Nat) variable (h : ∀ x : Nat, f x ≤ f (x + 1)) example : f 0 ≤ f 3 := have : f 0 ≤ f 1 := h 0 have : f 0 ≤ f 2 := Nat.le_trans this (h 1) -- this : f 0 ≤ f 1 show f 0 ≤ f 3 from Nat.le_trans (by assumption) (h 2) -- (by assumption)如果目标可以inferred,就用assumption代 -- by assumption的替代品那你,显示指出goal,notation "‹" p "›" => show p by assumption --`\f< \f>`
example : f 0 ≥ f 1 → f 1 ≥ f 2 → f 0 = f 2 := fun p :(f 0 ≥ f 1) => fun _ :(f 1 ≥ f 2) => -- 这个placeholder have : f 0 ≥ f 2 := Nat.le_trans ‹f 1 ≥ f 2 › ‹f 0 ≥ f 1 › have :f 0 ≤ f 2 := Nat.le_trans (h 0 ) (h 1)
Nat.le_antisymm this ‹f 0 ≥ f 2 ›
Tactics
by tactics
Entering Tactic Mode
1 2 3 4
/- `exact` is a variant of `apply` ,which signals that the expression given should fill the goal exactly.
-/
tatics 可能会产生多个子目标,并且tag them,可以在窗口中看到
1 2 3 4 5 6 7 8 9 10 11 12
-- In the case of `apply` tactic, the tags are inferred from the parameters' names used in the And.intro declaration.
-- can structure your tatics using the notation `case <tag> => <tatics>`,我认为这样可以改变解决默认的解决顺序 -- 也可以\. 结构化证明过程 -- And.intro {a b : Prop} (left : a) (right : b) : a ∧ b theorem test (p q : Prop) (hp : p) (hq : q) : p ∧ q ∧ p := by apply And.intro case right => apply And.intro case left => exact hq case right => exact hp case left => exact hp
/- tactic `intro` : introduces a hypothesis, a variable of any type, is a command for constructing function abstraction interactively (i.e., terms of the form fun x => e) 也可以像`match`一样提供多个参数,也可已使用`intros` intro | \<\> => ... | \<\> => ...
也可以不提供任何参数:in which case, it chooses names and introduces as many variables as it can.结果是Lean自动生成名字(不能访问),可以通过 use the combinator `unhygienic` to disable this restriction.
-/ example : ∀ a b c : Nat, a = b → a = c → c = b := by intros -- 相当于跟着a✝² b✝ c✝ a✝¹ a✝ rename_i a b c h1 h2 apply Eq.trans apply Eq.symm assumption assumption
example : ∀ a b c : Nat, a = b → a = c → c = b := by unhygienic intros -- 相当于跟着 a b c a_1 a_2 apply Eq.trans apply Eq.symm exact a_2 exact a_1 /- tactic `rename_i `:`rename_i x_1 ... x_n` : renames the last n inaccessible names using the given names. -/
/- tactic `assumption` : looks through the assumptions in context of the current goal, and if there is one matching the conclusion, it applies it. -/
/- The `rfl` tactic is syntactic sugar for `exact rfl` -/
/- The `revert` tactic is an reverse to `intro`, 把假设转为目标 -/
/-`generalize` tactic replace an arbitrary expression in the goal by a fresh variable: 将目标中的任意表达式替换成新变量 `generalize [h :]expre = variable`,但替换可能会导致goal不可证明-/ example : 2 + 3 = 5 :=by generalize h : 3 = x -- goal: 2 + x = 5 rw[← h] -- 2 +3 = 5已经证明完毕了
/-`admit` :tactic is the analogue相似 of the `sorry` tactics-/
/-`cases` : decompose any element of an inductively defined type,子目标解决顺序任意;对于子目标使用相同策略的证明有用,和`match`的语法类似 cases h(具有多个可能值) with ,我认为区别在于match需要用函数构造,而cases直接使用tag作为函数名了 其中h的选择:可以是某个定理名(来自库或文件),也可以是某个变量表达式 -/ | <tag1> => <tactics1> -- tag是Lean在声明中的参数名 | <tag2> => <tactics2> -/ -- cases h(有多种可能) with -- | inl hp => apply Or.inr hp -- inl是tag,hp是参数(根据此情况需要传入的参数) -- | inr hq => apply Or.inl; exact hq example (p q : Prop) : p ∧ q → q ∧ p := by intros h cases h with | intro left right => apply And.intro right left
example (p q : Prop) : p ∧ q → q ∧ p := by intros h match h with | And.intro left right => apply And.intro right left
-- 使用相同策略 example (p : Prop): p ∨ p → p := by intro h cases h repeat assumption
/-combinator `tac1 <;> tac2` : <;> operator provides a parallel version of the sequencing operation: tac1 is applied to the current goal, and then apply the tactic `tac2` to each subgoal produced by tactic `tac1` -/ example (p : Prop) : p ∨ p → p := by intro h cases h <;> assumption
/-`constructor`: apply the first applicable constructor of an inductively defined type
-/ example (p q : Prop) : p ∧ q → q ∧ p := by intros h match h with | And.intro left right => constructor; exact right; exact left -- constructor example (p q : Prop) : p ∧ q → q ∧ p := by intros h match h with | And.intro left right => constructor<;> assumption -- tac1 <;> tac2
-- case and constructor example (p q : Nat → Prop) : (∃ x, p x) → ∃ x, p x ∨ q x := by intros h cases h with | intro w hpw => constructor ; apply Or.inl; exact hpw
/-`contradiction`: contradiction closes the main goal if its hypotheses are "trivially contradictory". example (h : False) : q := by contradiction -/
-- “combine” `intro h` with `match h ...` example (p q r : Prop) : p ∧ (q ∨ r) ↔ (p ∧ q) ∨ (p ∧ r) := by apply Iff.intro . intro h match h with -- 可以推断出来 | And.intro _ (Or.inl _) => apply Or.inl (⟨by assumption ,by assumption⟩ ) -- 自动推断 by assumption | And.intro hp (Or.inr hr) => apply Or.inr (⟨hp ,hr⟩ ) . intro -- intro 和match 合二为一了 | Or.inl ⟨hp, hq⟩ => constructor; exact hp; apply Or.inl; exact hq | Or.inr ⟨hp, hr⟩ => constructor; exact hp; apply Or.inr; exact hr
Structuring Tactic Proofs
apply and exact 可以传入任意term,e.g. (using haveshow这两个属于 term-style的term…) can invoke tactic mode by inserting a by block,也有show 的tactic
example (p q r : Prop) : p ∧ (q ∨ r) → (p ∧ q) ∨ (p ∧ r) := by intro h by exact have hp : p := h.left have hqr : q ∨ r := h.right show (p ∧ q) ∨ (p ∧ r) by -- 此处是show .. by 是term-style cases hqr with | inl hq => exact Or.inl ⟨hp, hq⟩ | inr hr => exact Or.inr ⟨hp, hr⟩
/-`show` tactic: declares the type of the goal that is about to be solved,while remaining in tactic mode `show t` finds the first goal whose target unifies with t. It makes that the main goal, performs the unification, and replaces the target with the unified version of t.重写目标为定义上相等的东西 -/ example (n : Nat) : n + 1 = Nat.succ n := by show Nat.succ n = Nat.succ n rfl
/-`have` tactic: intoduces a new subgoal, have name_label : xxx := ... 1. can omit the label in the `have` tactic, in which case, the default label `this` is used 2. can omit types in the `have` tactic -/ example (p q r : Prop) : p ∧ (q ∨ r) → (p ∧ q) ∨ (p ∧ r) := by intro ⟨hp, hqr⟩ show (p ∧ q) ∨ (p ∧ r) cases hqr with | inl hq => have hpq: p ∧ q := And.intro hp hq-- 定义label apply Or.inl exact hpq | inr hr => have : p ∧ r := And.intro hp hr -- 省略label apply Or.inr exact this
/-`let` tactic: introduce local definitions the difference between `let` and `have` is that `let` introduces a local definition in the contextt, so that the definition of the local declaration can be unfolded in the proof. -- let a : Nat := 3 * 2 -- 类型可以省略 -/
/- notation`.` : 空格敏感,依赖于对齐来检测whether the tactic block ends.
不用`.` 也可以使用{} 和 ; 来确定block -/
看起来有结构的方式: .case label =>{}
1 2 3 4 5 6 7 8 9 10 11 12
/- apply foo . <proof of first goal> . <proof of second goal> case <tag of first goal> => <proof of first goal> case <tag of second goal> => <proof of second goal> { <proof of first goal> } { <proof of second goal> } -/
Tactic Combinators( are operations that form new tactics from old ones)
/- The `first | t₁ | t₂ | ... | tₙ` applies each `tᵢ` until one succeeds, or else fails
`skip` tactic does nothing and succeeds
-/ example (p q : Prop) (hp : p) : p ∨ q := by first | apply Or.inl; assumption | apply Or.inr; assumption
example (p q : Prop) (hq : q) : p ∨ q := by first | apply Or.inl; assumption | apply Or.inr; assumption
/- `try` tac runs tac and succeeds even if tac failed. !! `repeat (try tac)` will loop forver,because `try tac` will always succeed -/ example (p q r : Prop) (hp : p) (hq : q) (hr : r) : p ∧ q ∧ r := by -- 第一个constructor 将goal 变为 case left : p 和 case right : q ∧ r -- 第二个constructor只在case right 才会执行 constructor <;> (try constructor) <;> assumption end
/-`all_goals tac` : applies tac to all open goals -/ example (p q r : Prop) (hp : p) (hq : q) (hr : r) : p ∧ q ∧ r := by constructor all_goals (try constructor) all_goals assumption
/-`any_goals tac`: 与`all_goal`类似,except it succeeds if its argument succeed on at least one goal -/ example (p q r : Prop) (hp : p) (hq : q) (hr : r) : p ∧ ((p ∧ q) ∧ r) ∧ (q ∧ r ∧ p) := by repeat any_goals constructor all_goals assumption -- repeat (first | constructor | assumption )
/-`focus t `combinator ensures that `t` only effects the current goal,temporarily hiding the others from the scope.So, if t ordinarily only effects the current goal, focus (all_goals t) has the same effect as t. -/ example (p q r : Prop) (hp : p) (hq : q) (hr : r) : p ∧ q ∧ r := by constructor case right => constructor ; all_goals assumption case left => focus (all_goals apply hp) -- 等价于 focus (apply hp)
Rewriting
1 2 3 4
/-`rewrite [t]`: t is a term whose type asserts an equality. 1. 有多个可以替换时默认选择第一个进行替换 2. noatation `rw [t] at h` : 在h中rw -/
Using the Simplifier
A number of identities in Lean’s library have been tagged with the [simp] attribute, and the simp tactic uses them to iteratively rewrite subterms in an expression.也可以使用at h指定在h中应用 simp 对于交换和结合顺序的解释:the simplifier uses these two facts to rewrite an expression, as well as left commutativity. 对于local modifier告诉simplifier使用右结合, It may seem that commutativity and left-commutativity are problematic, in that repeated application of either causes looping。But the simplifier detects identities that permute their arguments, and uses a technique known as ordered rewriting. 也许是最后的goal会变成右结合,但simp会报错made no progress
1 2 3 4
-- 例子 example (w x y z : Nat) (p : Nat → Prop) : x * y + (z * w) * x = x * w * z + y * x := by simp
/-`simp` 1. do propositional rewriting. using hypothesis p, it rewrites p ∧ q to q and p ∨ q to true 2. 对于新的定义,也可以使用simp,可以在定义时标注 @[simp] (表示定理具有 [simp] 属性; 第二种方式是拉出来写 `attribute [simp] theorem_name`)就可以在使用`simp`时不显式指明定理 -/
example (p q : Prop) (hp : p) : p ∧ q ↔ q := by simp [*] -- 重写:p ∧ q = q example (x y z : Nat) : (x + 0) * (0 + y * 1 + z * 0) = x * y := by simp -- 使用了带有simp属性的等式
example (x y z : Nat) (p : Nat → Prop) (h : p (x * y)) : p ((x + 0) * (0 + y * 1 + z * 0)) := by simp at *; assumption
def mk_symm (xs : List α) := xs ++ xs.reverse theorem reverse_mk_symm (xs : List α) : (mk_symm xs).reverse = mk_symm xs := by simp [mk_symm]
example (xs ys : List Nat) : (xs ++ mk_symm ys).reverse = mk_symm ys ++ xs.reverse := by simp [reverse_mk_symm]
attribute [simp] reverse_mk_symm -- 这个属性一直存在(只要导入了含有属性声明的文件,约束属性作用域的办法: using the `local` modifier) -- attribute [local simp] reverse_mk_symm -- 局部属性,outside the section, the simplifier will no longer use this theorem by default
example (xs ys : List Nat) (p : List Nat → Prop) (h : p (xs ++ mk_symm ys).reverse) : p (mk_symm ys ++ xs.reverse) := by simp at h; assumption
/-“wildcard 通配符” *: 代表所有的hypotheses and the goal e.g. simp [*] -/
Split Tactic
The split tactic is useful for breaking nested if-then-else and match expressions in cases. For a match expression with n cases, the split tactic generates at most n subgoals. 拆分多种subgoal和match差不多
/-`simp_arith`: is shorthand for simp with arith := true and decide := true. This enables the use of normalization by linear arithmetic. -/ section def f (x y z: Nat): Nat := match x , y ,z with | 0, _, _ => y | _, 0, _ => z | _, _, 0 => x | _, _, _ => 1 example (x y z : Nat) : x ≠ 0 → y ≠ 0 → z ≠ 0 → z = w → f x y w = 1 := by intros -- f x y w = 1 simp [f] /-(match x, y, w with | 0, x, x_1 => y | x, 0, x_1 => w | x_1, x_2, 0 => x | x, x_1, x_2 => 1) = 1-/ split ;repeat (first | contradiction| rfl)
-- split <;> first | contradiction | rfl
-- split -- case h_1 => contradiction -- case h_2 => contradiction -- case h_3 => contradiction -- case h_4 => rfl end
by default, simp include all theorems that have been marked with the attribute [simp]. Writing simp only excludes these defaults, allowing you to use a more explicitly crafted list of rules. In the examples below, the minus sign and only are used to block the application of reverse_mk_symm.
1 2 3 4 5 6 7 8 9
example (xs ys : List Nat) (p : List Nat → Prop) (h : p (xs ++ mk_symm ys).reverse) : p ((mk_symm ys).reverse ++ xs.reverse) := by simp [-reverse_mk_symm] at h; assumption -- -负号
example (xs ys : List Nat) (p : List Nat → Prop) (h : p (xs ++ mk_symm ys).reverse) : p ((mk_symm ys).reverse ++ xs.reverse) := by simp only [List.reverse_append] at h; assumption -- only的应用
1 2 3 4 5 6 7 8 9 10
/-`simp` tactic 的useful configuration options: {contextual := true, arith := true, decide := true, ...} -/ -- 用x= 0 化简 y + x = y ,用x ≠ 0 化简 x ≠ 0 example : if x = 0 then y + x = y else x ≠ 0 := by simp (config := { contextual := true })
example : 0 < 1 + x ∧ x + y + 2 ≥ y + 1 := by simp_arith -- is a shorthand for `simp (config := {arith := true})`
Extensible Tactics
command syntax to define new tactics command macro_rules to specify what should be done when the new tactic is used. You can provide different expansions, and the tactic interpreter will try all of them until one succeeds.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
--define a new tactic `my_tac` syntax "my_tac" : tactic macro_rules | `(tactic| my_tac)=> `(tactic| assumption) example (h : p) : p := by my_tac
-- extend `my_tac` macro_rules | `(tactic | my_tac) => `(tactic| apply And.intro <;>my_tac ) example (x : α) (h : p) : x = x ∧ p := by my_tac
Interacting with Lean
Although the names of theorems and definitions have to be unique, the aliases that identify them do not._root_ is an explicit description of the empty prefix.
protected keyword用在定义时,可以禁止更短的别名(必须使用嵌套的全名)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
-- `open` 后面可以加括号指明至对于括号里面的打开命名空间,也就是可以直接使用括号里面的短别名 open Nat (succ zero gcd) #check zero #check add -- error #check Nat.add --对的
-- 让Nat.mul的别名为times, Nat.plus的别名为plus open Nat renaming mul → times, add → plus #eval plus (times 2 2) 3 -- 7
/-`exprot`: 在当前namespace中创建短别名,如果export在一个命名空间外使用,则创建的别名是the top level -/ export Nat (succ zero gcd)
/-`instance`command: assiggn the notation 小于等于 to some relation -/ def isPrefix (l₁ : List α) (l₂ : List α) : Prop := ∃ t, l₁ ++ t = l₂
instance : LE (List α) where le := isPrefix -- 变成局部 /- def instLe : LE (List α) := { le := isPrefix }
section attribute [local instance] instLe
example (as : List α) : as ≤ as := ⟨[], by simp⟩
end -/ theorem List.isPrefix_self (as : List α) : as ≤ as := ⟨[], by simp⟩
More on Implicit Arguments
1 2 3 4
/-annotion : `{}` `\{{ and \}}` : weaker than `{}`,表示一个占位符应该只被添加在下一个显示参数之前 -/
Notation and Precedence
语法扩展命令
1 2 3 4
/-introduing new `prefix` `infix` `postfix` operators-/ infixl : 65 "+" => HAdd.hAdd -- 65 is the precedence level -- initial_command_name(describing the operator kind) : parsing precedence(natural number) : "a_token" => a_fuction(the operator should be translated to ) -- 其中`precedence` 代表how "tightly" an operator binds to its arguments,encoding the order of operations(1024优先级可以简写为`max`)
1 2 3
/-`notation`: 接受一系列的tokens和命名的term placeholders with precedences -/
(剩下的不懂)
Coercions
强制转换,也有自动检测和插入强制转换的机制
1 2 3 4
variable (m n : Nat) variable (i j : Int) #check (m + i) --↑m + i : Int #check Int.ofNat m -- Int
Displaying Information
1 2
#check @Eq #print -- (展示type of the symbol and definition of a definition or theorem; 展示indicates that fact, and shows the type of a constant or an axiom)
Setting Options
1 2 3
-- set_option <name> <value> -- `set_option pp.all true` carries out these settings all at once, whereas `set_option pp.all` false reverts to the previous values. -- pp.notation :使用defined notation to display output
Using the Library
import 可以传递,But the act of opening a namespace, which provides shorter names, does not carry over. In each file, you need to open the namespaces you wish to use. 标准库1 Lean4标准库2
Auto Bound Implicit Arguments
Lean4 的新特性: 对于是single lower case or greek letter的未绑定标识符,Lean会自动绑定为implicit argument 可以通过set_option autoImplicit false禁止此特性
-- 需要括号打断· 和 · #check (Prod.mk · (· + 1)) --fun x => (x, fun x => x + 1) : ?m.49894 → ?m.49894 × (Nat → Nat)
Named Arguments
命名参数用来不按顺序传参,provide the value for an implicit parameter when lean failed to infer it. 可以使用.. to provide missing explicit arguments as _,前提是这些显示参数是Lean可以自动推断出来
-- Inductive type is built up from a specified list of constructors inductive Foo where | constructor₁ : ... → Foo | constructor₂ : ... → Foo ... | constructorₙ : ... → Foo
/-使用match定义函数:-/ namespace Hidden def and (a b : Bool) : Bool := match a with | true => b | false => false end Hidden
Constructors With Arguments
Remember that the standard library defines notation α × β for Prod α β and (a, b) for Prod.mk a b.
Notice that the product type depends on parameters α β : Type which are arguments to the constructors as well as Prod. Lean detects when these arguments can be inferred from later arguments to a constructor or the return type, and makes them implicit in that case. Lean 可以自动推断参数类型后使其隐式化
in this section : each constructor relies only on previously specified types.
A type, like Prod, that has only one constructor is purely conjunctive :the constructor simply packs the list of arguments into a single piece of data, essentially a tuple where the type of subsequent arguments can depend on the type of the initial argument. We can also think of such a type as a “record” or a “structure”. In Lean, the keyword structure can be used to define such an inductive type as well as its projections, at the same time.只有一个构造器的纯粹or类型:将参数打包成一个数据,后续参数都依赖于第一个参数, 这样的类型可以看作是record或者structure
structure Color where (red : Nat)(green : Nat)(blue : Nat) -- 不用()用换行也行 deriving Repr
-- dependent Product sugma -- Sigma.mk.{u, v} {α : Type u} {β : α → Type v} (fst : α) (snd : β fst) : Sigma β namespace Hidden inductive Sigma {α : Type u} (β : α → Type v) where | mk : (a : α) → β a → Sigma β end Hidden
Inductively Defined Propositions
归纳类型定义的类型可以生存在任何类型的universe中,包括Prop
1 2 3
inductive Or (a b: Prop) where | inl : a -> Or a b | inr : b -> Or a b
Roughly speaking, what characterizes inductive types in Prop is that one can only eliminate to other types in Prop. This is consistent with the understanding that if p : Prop, an element hp : p carries no data.
∃ x : α, p is syntactic sugar for Exists (fun x : α => p).
1 2
inductive Exists {α : Type u} (p : α → Prop) where | intro (w : α) (hw : p w) : Exists p
The notation {x : α // p x}也表示一个类型 is syntactic sugar for Subtype (fun x : α => p x)表示对所有的x \a,p x都成立. It is modeled after subset notation in set theory: the idea is that {x : α // p x} denotes the collection of elements of α that have property p.
inductive Nat where | zero : Nat | succ : Nat → Nat deriving Repr
def add (a b : Nat): Nat := match a with | Nat.zero => b | Nat.succ a' => Nat.succ (add a' b)
-- 归纳证明例子 open Nat theorem add_comm (m n : Nat) : m + n = n + m := Nat.recOn (motive := fun x => m + x = x + m) n (show m + 0 = 0 + m by rw [Nat.zero_add, Nat.add_zero]) (fun (n : Nat) (ih : m + n = n + m) => show m + succ n = succ n + m from calc m + succ n _ = succ (m + n) := rfl _ = succ (n + m) := by rw [ih] _ = succ n + m := sorry)
-- sorry 处依旧可以用归纳证明 succ n + 0, succ n + succ m
Other Recursive Data Types
Inductive Types with Parameters
1 2 3
inductive List (α : Type u) where | nil : List α | cons : α → List α → List α
Inductive Types with Recursion
1
Tactics for Inductive Types
cases (当open Classical, can use the law of the exclude middle for any proposition at all),
1 2 3
-- 排中律Decidable.em theorem em (p : Prop) [Decidable p] : p ∨ ¬p := byCases Or.inl Or.inr
induction tactic: 用来归纳证明
证明归纳
支持 user-defined induction principle with multiple targets 语法与cases一样,except that the argument can only be a term in the local context,也可以用case 替换 |
1 2 3 4
theorem zero_add (n : Nat) : 0 + n = n := by induction n with | zero => rfl -- case zero => rfl | succ n ih => rw [Nat.add_succ, ih]
mutual inductive Tree (α : Type u) where | node : α → TreeList α → Tree α
inductive TreeList (α : Type u) where | nil : TreeList α | cons : Tree α → TreeList α → TreeList α end
Nested : 使用其他type
1 2
inductive Tree (α : Type u) where | mk : α → List (Tree α) → Tree α -- 用List(Tree \a )代替TreeList
Induction and Recursion
Pattern Matching
1 2 3 4 5 6 7 8 9 10
/-`zero` and `succ` notation 具有 [match_pattern] attribute, they can be used in pattern matching -/ -- def的多cases形式 def sub1 : Nat → Nat | zero => zero | succ x => x -- 不用zero和succ def sub1' : Nat → Nat | 0 => 0 | x +1 => x
认为模式匹配是多情况定义 Pattern matching works with any inductive type, such as products and option types fun_name.match_1:看定义
/-出现在`:` 之前的参数不参与pattern matching,Lean also allows parameters to occur after `:`,but it cannot pattern match on them-/ -- α 不参与 pattern matching def tail1 {α : Type u} : List α → List α | [] => [] | a :: as => as -- α也没有参与 pattern matching,只对list进行了分割 def tail2 : {α : Type u} → List α → List α | α, [] => [] | α, a :: as => as
Wildcards and Overlapping Patterns
the notion of a wildcard
对于patterns overlap的情况,Lean use the first applicable equation
1 2 3 4 5 6 7 8 9 10
def foo : Nat → Nat → Nat | 0, n => 0 | m, 0 => 1 | m, n => 2
function programming languages support incomplete patterns.We can simulate the arbitrary value approch using the Inhabited type class.
we will see that Lean can be instructed that suitable base types are inhabited, and can automatically infer that other constructed types are inhabited. On this basis, the standard library provides a default element, default, of any inhabited type.inhabited 类型有默认值,可以传递?
1 2 3 4 5 6 7
/-`Option`: simulate the incomplete patterns.The idea is to return `some a`for the provided patterns, and use `none` for the incomplete cases
-/ def f2 : Nat → Nat → Option Nat | 0, _ => some 1 | _, 0 => some 2 | _, _ => none -- the "incomplete" case
Structural Recursion and Induction
structurally recursive definitions
well-founded recursive definitions
mutually recursive definitions
1 2 3 4 5 6 7 8 9 10 11 12 13 14
-- 模式匹配 /- As we have seen, a `pattern` is either a variable, a constructor applied to other patterns, or an expression that normalizes to something of that form (where the non-constructors are marked with the [match_pattern] attribute). The appearances of constructors prompt case splits, with the arguments to the constructors represented by the given variables. `pattern`: 1. 变量 2. 构造器 3. 表达式 4. explicit terms in patterns that are needed to make an expression type check,though they do not play a role in pattern matching. These are called "inaccessible patterns" for that reason. -/ def foo (a : α) : (b : β) → γ | [patterns₁] => t₁ ... | [patternsₙ] => tₙ
In other situations, however, reductions hold only propositionally, which is to say, they are equational theorems that must be applied explicitly. The equation compiler generates such theorems internally. They are not meant to be used directly by the user; rather, the simp tactic is configured to use them when necessary.
1 2 3 4 5 6 7 8 9 10 11
theorem zero_add : ∀ n, add zero n = n | zero => rfl | succ n => congrArg succ (zero_add n) -- 递归 -- | succ n => by simp [add, zero_add] -- 要证明 add zero (succ n) = succ n => --f :=succ x = x + 1; -- h:= add zero n = n 结果 succ (add zero n) = succ n -- 另一种写法 theorem zero_add : ∀ n, add zero n = n | zero => by simp [add] | succ n => by rw[add, zero_add]
与模式匹配的定义一样:parameters to a structural recursion or induction 可能出现在冒号之前Such parameters are simply added to the local context before the definition is processed
def fibFast' (n : Nat): Nat:= let rec loop : Nat → Nat × Nat | 0 => (0,1) | n + 1 => let p := loop n; (p.2, p.2 + p.1) (loop n).2
/-`below` `brecOn`介绍: To handle structural recursion, the equation compiler uses course-of-values recursion, using constants below and brecOn that are automatically generated with each inductively defined type -/ variable (C : Nat → Type u)
#check (@Nat.below C : Nat → Type u)
#reduce @Nat.below C (3 : Nat)-- 存储C 0, C 1, C 2元素的数据结构
-- 下面是值过程递归的例子 #check (@Nat.brecOn C : (n : Nat) → ((n : Nat) → @Nat.below C n → C n) → C n)
The use of course-of-values recursion is one of the techniques the equation compiler uses to justify to the Lean kernel that a function terminates. It does not affect the code generator which compiles recursive functions as other functional programming language compilers. Recall that #eval fib <n> is exponential on . On the other hand, #reduce fib <n> is efficient because it uses the definition sent to the kernel that is based on the brecOn construction.brecOn的实现可能更有效率
– 不懂! 这里面的::是什么语法
1 2 3
def append : List α → List α → List α | [] , bs => bs | a :: as , bs => a :: append as bs
In lean,in a sequence of implications there are implicit parentheses grouped to the right.
it is common in Lean to use curly brackets to make quantified variables implicit when they can be inferred from subsequent hypotheses.So we can just do lemma to the hypothese without mentioning the objects.即用{}括起来的在调用时不需要显示指出。
5.2 Induction and Recursion
1 2 3 4 5 6 7 8 9
-- 归纳 -- inductive types (which are types generated infuctively by a given list of constructors) -- e.g. the natual numbers are declared as follows( in Prelude.lean) inductive Nat where | zero : Nat -- constructor | succ (n : Nat): Nat -- constructor ---------------------------------------------------------
@[simp] -- specifys that the defining equation should be added to the database of identities that the simplifier uses by default
6.1 Defining Structures
structure can specify not only data types but also constraints that the data must satisfy(In lean,these are represented as fields of type Prop). def 可以用来定义structure实例 subtype类型({x: alpha // p x}表示for all a in alpha,p x is true) construction combines a piece of data with a property.
structure的好处:But even though we can use products, subtypes, and Sigma types instead of structures, using structures has a number of advantages. Defining a structure abstracts away the underlying representation and provides custom names for the functions that access the components. This makes proofs more robust: proofs that rely only on the interface to a structure will generally continue to work when we change the definition, as long as we redefine the old accessors in terms of the new definition. Moreover, as we are about to see, Lean provides support for weaving structures together into a rich, interconnected hierarchy, and for managing the interactions between them.
1 2
structure structure_name where ...
annotation when define a structure:
structure can bundle together data,so as the and符号
1 2
-- 此注解表示自动生成定理: 当结构体的两个实例组成部分对应相同时,这两个实例相等 @[ext] -- extensionality,tells Lean to automatically generate theorems that can be used to prove that two instances of a structure are equal when their components are equal.
define particular instances of structure
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
def inst_name : struc_name where ... -- e.g. x:=1 --------------------------------- def inst_name : struct_name := ... --e.g. <value1 , ,...> -- anonymous constructor notation --------------------------------- def inst_name := struc_name.mk ...valuek valuek+1 ... -- struc_name.mk 是constructor,也可以自己命名,需要在define structure 的时候,比where多 了name和:: -- structure struc_name where constructor_name :: -- ... -- open a namespace namespace namespace_name ... end namespace_name
definations and theorems
about namespace: when namespace not open, can use anonymous projection notation,which allows us to write a.add b instead of namespace_name.add a b.
protected keyword
1 2 3
-- so that the theorem name is Point.add_comm,even when the namespace is open protected theorem add_comm (a b :Point) :add a b = add b a:=by sorry
6.2 Algebraic Structures
abelian or commutative group : set A, operation •,假设a b ∈ A,则需要满足Closure (a · b ∈ A),Associativity(结合律),Identity element (必存在e∈A就是元 使得a · e = e · a = a),Inverse element(对任意a,都有a · b = b · a = e),Commutativity(交换律) lattice : 带有join和meet(leat upper bound 和greatest lower bound)偏序集合 ring : 加法abelian group和an associative multiplication operation *,和identity 1,且multiplication distributes over addition.如果乘法是交换的则环是交换的
Lean theorem
multiple arrows:隐式右结合(associate to the right)
Lean使用快捷键
Ctrl-Shift-P : get access to the Lean 4:show all abbreviations Ctrl-Space : completion in the editior to guess the theorem name
写法
运算的特殊写法
divisibility relation整除关系 |
提示写法
“apply?”可以在右边得到suggestions
重复证明过程的写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
--1. 使用hypothesis :任意 --2. 使用repeat example : min a b = min b a := by have h : ∀ x y : ℝ, min x y ≤ min y x := by intro x y apply le_min apply min_le_right apply min_le_left apply le_antisymm apply h apply h ----------------------- example : min a b = min b a := by apply le_antisymm repeat apply le_min apply min_le_right apply min_le_left
name_of_theorem
A_of_B_of_C: established something of the form A from hypothese of the form B and C,where A,B,C approximate the way we might read the goals out loud,e.g. x+y<… probably start with add_lt.
example use by & not use by
1 2 3 4 5 6 7
example (a b : R) : a - b = a + -b :=by rw[sub_eq_add_neg a b] end
example (a b : R) : a - b = a + -b := sub_eq_add_neg a b end
语法记录
Command
1 2 3 4 5 6 7 8 9 10 11 12
-- section ... end -- 变量声明 variable(v1 v2 v3 ... : type) -- 检查类型,或描述用法 #check ... #check a -- a:R #check mul_comm -- mul_comm.{u_1} {G : Type u_1} [CommMagma G] (a b : G) : a * b = b * a
#check norm_num -- 常量计算与比较,Normalize numerical expressions. Supports the operations + - * / ⁻¹ ^ and % over numerical types such as ℕ, ℤ, ℚ, ℝ, ℂ and some general algebraic types, and can prove goals of the form A = B, A ≠ B, A < B and A ≤ B, where A and B are numerical expressions. It also has a relatively simple primality prover.
-- Nat.gcd m n :最大公约数,在显示的时候会显示出来m.gcd.n
rfl is short for “reflexivity”.
calc先定义阶段目标再填写过程
1 2 3 4 5 6 7 8
example:(a+b)*(a+b) = a*a+2*a*b +b*b: calc xx :by xx _=xxx:=by xx _=xxxx:=by xxxx
have 先提出假设h进行证明,之后可以使用h推断goal
1 2 3 4
theorem zero_mul(a: R):0*a =0 := by have h: 0*a+0*a = 0*a+0 :=by rw[←add_mul,add_zero,add_zero] rw[add_left_cancel h]
automation for that follow purely from the ring axioms
1 2
example: c*b*a = b*(a*c):= by ring
a ring consists of a collection of objects, R,(R with + is an abelian group,with 0 as the additive identity , with negation as inverse) operations + *,and (multiplication is associative with identity 1,and multiplication distributes over addition) constants 0 and 1,and an operation x->-x
Lean is good not only for proving things about concrete mathematical structures like the natural numbers and the integers, but also for proving things about abstract structures, characterized axiomatically, like rings. Moreover, Lean supports generic reasoning about both abstract and concrete structures, and can be trained to recognize appropriate instances.
sytax model Lean’s parser produces a concrete syntax tree,of type Lean.Syntax(is an inductive type that represents all of Lean’s syntax,including commands,terms,tactics , and any custom extensions)all of those are basic building blocks: Atoms(fundamental terminals),Identifiers,Nodes(the parsing of nonterminals,contain a syntax kind), Missing Syntax(error mess) Constructors of Lean.Syntax:missing,node,atom,ident
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/- Copyright (c) 2023 Eric Wieser. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Eric Wieser -/ syntax (name := eqns) "eqns" ident* : attr
initialize eqnsAttribute : NameMapExtension (Array Name) ← registerNameMapAttribute { name := `eqns descr := "Overrides the equation lemmas for a declation to the provided list" add := fun | _, `(attr| eqns $[$names]*) => -- 这里面的第二个参数是模式匹配,捕获了eqns关键字后面的所有标识符 pure <| names.map (fun n => n.getId) | _, _ => Lean.Elab.throwUnsupportedSyntax }
initialize Lean.Meta.registerGetEqnsFn (fun name => do pure (eqnsAttribute.find? (← getEnv) name))
本来是def M : ℕ → Matrix m n ℕ def M : ℕ → Matrix (Fin 3) (Fin 3) ℕ – 类型不匹配的原因之一:没有指定矩阵的具体维数 | 0 => Matrix.of (fun i j => if (i = j) then 1 else 0) | 1 => Matrix.of (fun i j => if (i = j) then 2 else 0) | _ => Matrix.of (fun i j => 3)
使用栈计算表达式时,‘)’ had different precedence levels,depending upon whether or not it was already on the stack,you had to give it one value before you put it on the stack, and another to decide when to take it off.(不明白为啥右括号有不同优先级,咋还能入栈) difference between interpreter and compiler : the recognizing of procedure, in the interpreter the recognizing procedure end up being coded as FUNCTIONS that return numeric values to their callers.None of the parsing routines for our compiler did that(不明白解释器和编译器在procedure翻译上的不同) assembler: 目的是生成object code,normally does that on a one-to-one basis(one object instruction per line of source code),but almost ebery assember also permits expression as arguments.In this case, the expression are always constant expressions, and so the assembler isn’t supposed to issue object code for them. Rather, it “interpreters” the expression and computes the corresponding constant result,which is what it actually emits as object code(此处说的汇编器对常量表达式的处理是“lazy”概念,exap: x = x+3-2-1最后生成时是对 x= x+0甚至是x=x)
阶段1 Single Num expression
只含”+-“的二元表达式
1 2 3 4 5 6 7 8 9 10 11 12 13 14
{ expr::= term<+-term> term::=GetNum } {-------------------------------------------------------------} procedureExpression; begin Term;{MOVE #GetNum,D0;} EmitLn('MOVE D0,D1'); case Look of '+': Add;{Match('+');Term;ADD D1,D0;结果存在D0} '-': Substract;{Match('-');Term;SUB D1,D0;NEG D0;结果存在D0} else Expected('加减操作符'); end;
多个操作数
1 2 3 4 5 6 7 8 9 10 11 12 13 14
{循环解决,将数压栈解决寄存器有限} {-------------------------------------------------------------} procedureExpression; begin Term;{MOVE #GetNum,D0;} while Look in ['+','-'] dobegin EmitLn('MOVE D0,-(SP)');{D1=D0;} case Look of '+': Add;{Match('+');Term; ADD (SP)+,D0;} '-': Substract;{Match('-');Term; SUB -(SP),D0; NEG D0 ;} else Expected('加减操作符'); end; end; end;
{ 优先级高,单位更小 expr::= term <+- term> term::= factor <*/ factor> factor::= GetNum } {------------------------------------------------------------} procedureExpression; begin Term; while Look in ['+','-'] dobegin EmitLn('MOVE D0,-(SP)');{压栈} case Look of '+': Add;{Match('+');Term; ADD (SP)+,D0;} '-': Substract;{Match('-');Term; SUB (SP)-,D0; NEG D0;} else Expected('加减操作符'); end; end; end;
procedureTerm; begin Factor;{GetNum;} while Look in ['*','/'] dobegin case Look of '*': Multiply;{Match('*');Factor;MUL (SP)+,D0;} '/': Divide; else Expected('乘除操作符'); end; end;
增加”()”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
{ 递归 factor ::= (expr) or GetNum } {------------------------------} procedureFactor; begin if Look ='(' than begin Match('('); Expression; Match(')'); end else EmitLn('MOVE'+#GetNum+',D0'); end;
增加”-+作为正负号的数”
增加0-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
{ expr::=- or term<+-term> } {------------------------------------------------------------} procedureExpression; begin if Look in ['+','-'] do EmitLn('CLR D0');{D0=0;} else term; while Look in ['+','-'] dobegin EmitLn('MOVE D0,-(SP)');{压栈} case Look of '+': Add;{Match('+');Term; ADD (SP)+,D0;} '-': Substract;{Match('-');Term; SUB (SP)-,D0; NEG D0;} else Expected('加减操作符'); end; end; end;
{ factor ::= GetNum | (expre) | Ident Ident ::= GetName <'('')'> } {-------------------------------------------} procedureFactor; begin if Look ='(' than begin Match('('); Expression; Match(')'); end elseif IsAlpha(Look) thenbegin Ident;{标识符识别} end else EmitLn('MOVE'+#GetNum+',D0'); end;
{----------------------------------------} procedureIdent; varName:Char; begin Name :=GetName; if Look = '('then begin Match('('); Match(')'); EmitLn('BSR '+Name);{调用函数} end else EmitLn('MOVE '+Name+'(PC),D0');{将操作数取出来放在D0} end;
{ GetNum,GetName重写while } function GetNum:string; var Value:string; begin Value:=''; ifnot isDigit(Look) then Expected('Digit'); while isDigit(Look) dobegin Value:=Value+Look; GetChar; end; end;
增加空格过滤
1 2 3 4 5 6 7 8 9
{ 识别空格,过滤空格,保证其余的识别开头第一个是非空格字符,即:
Init 中GetChar后要SkipWhite;
GetNum/Name 最后要SkipWhite; tips: LR (#10,^J) CR($13,^M) }
阶段3:简单的interpreter
interpreter就是等到必要时才打印,否则就一直计算
识别表达式
1 2 3
expr := -/+ term <+/- term> term := factor <*/ factor> factor := GetNum | (expr)
{-------------------------} {GetNum} functionGetNum:integer; begin if not isDigit(Look) then Expected('Integer'); GetNum := Ord(Look)-Ord('0'); GetChar; end;
{----------------------------} {Factor} functionFactor:integer; begin if Look = '(' then begin Match('('); Factor := Expression; Match(')'); end else Factor := GetNum; end;
{-------------------------------} { Term } functionTerm:integer; var Value :integer; begin Value := Factor; while isMulop(Look) do begin case Look of '*': begin Match('*'); Value:=Value * Factor; end; '/': begin Match('/'); Value:=Value div Factor; end; end; end; Term := Value; end;
{-----------------------------} {Expression} functionExpression:integer; var Value:integer; begin if isAddop(Look) thenbegin{能省略吗?不能因为省略后无法识别Term开头的表达式} Value:=0; end else Value:= Term; while isAddop(Look) dobegin case Look of '+': begin Match('+'); Value := Value +Term; end; '-': begin Match('-'); Value := Value -Term; end; end; end Expression := Value; end;
多位数字(改动GetNum)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
procadure GetChar; begin Read(Look); end; function GetNum:integer; var Value: integer; begin Value :=0; ifnotisDigit(Look) then Expected("Integer"); whileisDigit(Look)do begin Value := Value * 10 +Ord(Look)-Ord('0'); GetChar; end; GetNum := Value; end;
{ 在我的电脑上windows的WSL上NewLine只用了LF 结束符. 输入符号表命令 ?Name Value 输出符号表命令 !Name 对于WhiteSpace(在一个单元结束下一个单元开始前跳过中间的空格):最小读取单元是数字或者单个字符,故在init的GetChar后添加skipWhite,在GetNum和GetName结束添加skipWhite(读到第一个非空格字符),还需要在match后加上skipWhite } { 符号表 } var Table['A'..'Z'] of integer; { Input Table} procedureInput; begin Match('?'); Read(Table[GetName]); end; {Output} procedureOutput; begin Match('!'); Write(Table[GetName]); end; procedureNewLine; begin if isNewLine(Look) then begin GetChar; SkipWhite; end; end; { Main } begin repeat case Look of '?': Input; '!': Output; else Assignment; end; NewLine; until Look = '.'; end.