This commit is contained in:
Jon Eugster
2022-11-28 17:48:45 +01:00
parent 5cb0ff4ccf
commit e43a2e2e9f
9 changed files with 334 additions and 86 deletions
@@ -0,0 +1,35 @@
import TestGame.Metadata
Game "Introduction"
World "Tactic"
Level 1
Title "Aller Anfang ist... ein Einzeiler?"
Introduction
"
Willkommen zum Lean-Crashkurs wo du lernst wie man mathematische Beweise vom Computer
unterstützt und verifiziert schreiben kann.
Ein Beweis besteht in Lean aus verschiedenen **Taktiken**, welche ungefähr einem
logischen Schritt entsprechen, den man auf Papier aufschreiben würde.
Rechts im **Infoview** siehst den Status des aktuellen Beweis.
Du siehst ein oder mehrere offene **Goals** (mit einem `⊢` davor), die du noch zeigen musst.
Wenn du eine Taktik hinschreibst, dann versucht Lean diesen Schritt beim
ersten offenen Goal zu machen.
Wenn der Beweis komplett ist, erscheint \"goals accomplished\".
"
Statement : 42 = 42 := by
rfl
Message : 42 = 42 =>
"Die erste Taktik ist `rfl`, die ein Goal von der Form `A = A` beweist."
Hint : 42 = 42 =>
"Man schreibt eine Taktik pro Zeile, also gib 'rfl' ein gefolgt von ENTER."
Conclusion "Bravo!"
Tactics rfl
@@ -0,0 +1,28 @@
import TestGame.Metadata
Game "Introduction"
World "Tactic"
Level 2
Title "Definitionally equal"
Introduction
"
Achtung: `refl` kann auch Gleichungen beweisen, wenn die beiden Terme Lean-intern gleich
definiert sind, auch wenn diese unterschiedlich dargestellt werden.
So sind `1 + 1` und `2` per Definition das Gleiche, da sie beide von Lean als `0.succ.succ`
gelesen werden.
Das kann anfänglich verwirrend sein und das Verhalten hängt von der Lean-Implementation ab.
"
Statement : 1 + 1 = 2 := by
rfl
Conclusion
"
Im weiteren führen die meisten anderen Taktiken `refl` automatisch am Ende aus,
deshalb musst du dieses häufig gar nicht mehr schreiben.
"
Tactics rfl
@@ -0,0 +1,57 @@
import TestGame.Metadata
Game "Introduction"
World "Tactic"
Level 3
Title "Annahmen"
Introduction
"
Um Aussagen zu formulieren braucht man Annahmen. Zum einen sind das Objekte, von denen
eine Aussage handelt, wie zum Beispiel \"sei `n` eine natürliche Zahl\", oder
\"seien `A`, `B` logische Aussagen\", zum anderen sind dass Annahmen wie \"angenommen
dass `n` nicht Null ist\" oder \"angenommen `A` ist wahr\".
In Lean schreibt man beides mit der gleichen Notation: `(n : ℕ)` ist eine natürliche Zahl,
`(A B : Prop)` sind Aussagen, `(h : n ≠ 0)` ist eine Annahme, dass `n` nicht Null ist, und
`(hA : A)` ist die Annahme, dass `A` wahr ist (`hA` ist ein Beweis von `A`).
Die Annahmen kommen dann vor den Doppelpunkt.
Wenn eine Annahme `h` genau dem Goal entspricht, kannst du `exact h` verwenden.
"
Statement theorem not_or (n : ℕ) (h : n = 0) : n = 0 := by
assumption
-- TODO: In Lean3 hatten wir ein Lemma-Text. Können wir den wieder haben?
-- TODO: Macht es Sinn mehrere Aufgaben auf einer Seite zu haben?
Statement (A : Prop) (hA : A) : A := by
assumption
@[description "halli hallo"]
Statement instance hallo : Hallo Nat where
mul_eq_add := by
$editor {
rfl
rw }
add_comm := by
rfl
apply
Statement "halli hallo" instance hallo : Hallo Nat where
mul_eq_add := by
$editor {
rfl
rw }
add_comm := by
rfl
apply
Conclusion ""
Tactics assumption
@@ -0,0 +1,56 @@
import TestGame.Metadata
Game "Introduction"
World "Tactic"
Level 4
Title "Rewrite"
Introduction
"
Oft sind aber die Annahmen nicht genau das, was man zeigen will, sondern helfen einem
nur, dorthin zu kommen.
Wenn man eine Annahme `(h : X = Y)` hat, die sagt, dass `X` und `Y` gleich sind,
kann man die Taktik `rw` (steht für 'rewrite') brauchen um im Goal
das eine durch das andere zu ersetzen.
"
Statement (a b c d : ℕ) (h₁ : c = d) (h₂ : a = b) (h₃ : a = d) : b = c := by
rewrite [h₁]
rw [←h₂]
assumption
-- Gleich am Anfang anzeigen.
Message (a : ℕ) (b : ℕ) (c : ℕ) (d : ℕ) (h₁ : c = d) (h₂ : a = b) (h₃ : a = d) : b = c =>
"Wenn man eine Annahme `(h₁ : c = d)` hat, kann man mit `rw [h₁]` (oder `rewrite [h₁]`) das erste
`c` im Goal mit `d` ersetzen."
Hint (a : ℕ) (b : ℕ) (c : ℕ) (d : ℕ) (h₁ : c = d) (h₂ : a = b) (h₃ : a = d) : b = c =>
"Die kleinen Zahlen `h₁ h₂ h₃` werden in Lean oft verwendet und man schreibt diese mit
`\\1`, `\\2`, `\\3`, …"
Message (a : ℕ) (b : ℕ) (c : ℕ) (d : ℕ) (h₁ : c = d) (h₂ : a = b) (h₃ : a = d) : b = d =>
"Mit `rw [← h₂]` (`\\l`, also klein L wie \"left\") kann man eine Hypotheses
`(h₂ : a = b)` rückwärts anwenden und `b` durch `a` ersetzen."
-- TODO: Muss ich das wirklich mehrmals auflisten?
Message (a : ℕ) (b : ℕ) (c : ℕ) (d : ℕ) (h₁ : c = d) (h₂ : a = b) (h₃ : a = d) : a = a =>
"Der Hauptunterschied zwischen `rw` und `rewrite` ist, dass das erste automatisch versucht,
anschliessend `rfl` anzuwenden. Bei `rewrite` musst du `rfl` explizit noch aufrufen."
Message (a : ℕ) (b : ℕ) (c : ℕ) (d : ℕ) (h₁ : c = d) (h₂ : a = b) (h₃ : a = d) : b = b =>
"Der Hauptunterschied zwischen `rw` und `rewrite` ist, dass das erste automatisch versucht,
anschliessend `rfl` anzuwenden. Bei `rewrite` musst du `rfl` explizit noch aufrufen."
Message (a : ℕ) (b : ℕ) (c : ℕ) (d : ℕ) (h₁ : c = d) (h₂ : a = b) (h₃ : a = d) : c = c =>
"Der Hauptunterschied zwischen `rw` und `rewrite` ist, dass das erste automatisch versucht,
anschliessend `rfl` anzuwenden. Bei `rewrite` musst du `rfl` explizit noch aufrufen."
Message (a : ℕ) (b : ℕ) (c : ℕ) (d : ℕ) (h₁ : c = d) (h₂ : a = b) (h₃ : a = d) : d = d =>
"Der Hauptunterschied zwischen `rw` und `rewrite` ist, dass das erste automatisch versucht,
anschliessend `rfl` anzuwenden. Bei `rewrite` musst du `rfl` explizit noch aufrufen."
Conclusion "Übrigens, mit `rw [h₁] at h₂` kann man auch eine andere Annahme umschreiben
anstatt dem Goal."
-- TODO: Das macht es doch unmöglich mit den Messages...
Tactics assumption
--Tactics rw
@@ -0,0 +1,59 @@
import TestGame.Metadata
Game "Introduction"
World "Tactic"
Level 5
Title "Implikation"
Introduction
"
Wie wir schon gesehen haben, wir eine logische Aussage als `(A : Prop)` geschrieben, und
die Annahme, dass `A` wahr ist als `(hA : A)`, also `hA` ist sozusagens ein Beweis der
Aussage `A`.
Logische Aussagen können einander implizieren. Wir kennen hauptsächlich zwei Zeichen dafür:
`A ↔ B` (`\\iff`) bedeutet \"Genau dann wenn\" und `A → B` (`\\to`) bedeutet \"`A` impliziert `B`\".
Wenn man Aussage `B` beweisen will und eine Implikationsannahme `(h : A → B)` hat, dann kann man
diese mit `apply h` anwenden.
Auf Papier würde man schreiben, \"es genügt zu zeigen, dass `A` stimmt, denn `A` impliziert `B`\".
"
Statement (A B : Prop) (hA : A) (g : A → B) : B := by
apply g
assumption
-- Gleich am Anfang anzeigen.
Message (a : ℕ) (b : ℕ) (c : ℕ) (d : ℕ) (h₁ : c = d) (h₂ : a = b) (h₃ : a = d) : b = c =>
"Wenn man eine Annahme `(h₁ : c = d)` hat, kann man mit `rw [h₁]` (oder `rewrite [h₁]`) das erste
`c` im Goal mit `d` ersetzen."
Hint (a : ℕ) (b : ℕ) (c : ℕ) (d : ℕ) (h₁ : c = d) (h₂ : a = b) (h₃ : a = d) : b = c =>
"Die kleinen Zahlen `h₁ h₂ h₃` werden in Lean oft verwendet und man schreibt diese mit
`\\1`, `\\2`, `\\3`, …"
Message (a : ℕ) (b : ℕ) (c : ℕ) (d : ℕ) (h₁ : c = d) (h₂ : a = b) (h₃ : a = d) : b = d =>
"Mit `rw [← h₂]` (`\\l`, also klein L wie \"left\") kann man eine Hypotheses
`(h₂ : a = b)` rückwärts anwenden und `b` durch `a` ersetzen."
-- TODO: Muss ich das wirklich mehrmals auflisten?
Message (a : ℕ) (b : ℕ) (c : ℕ) (d : ℕ) (h₁ : c = d) (h₂ : a = b) (h₃ : a = d) : a = a =>
"Der Hauptunterschied zwischen `rw` und `rewrite` ist, dass das erste automatisch versucht,
anschliessend `rfl` anzuwenden. Bei `rewrite` musst du `rfl` explizit noch aufrufen."
Message (a : ℕ) (b : ℕ) (c : ℕ) (d : ℕ) (h₁ : c = d) (h₂ : a = b) (h₃ : a = d) : b = b =>
"Der Hauptunterschied zwischen `rw` und `rewrite` ist, dass das erste automatisch versucht,
anschliessend `rfl` anzuwenden. Bei `rewrite` musst du `rfl` explizit noch aufrufen."
Message (a : ℕ) (b : ℕ) (c : ℕ) (d : ℕ) (h₁ : c = d) (h₂ : a = b) (h₃ : a = d) : c = c =>
"Der Hauptunterschied zwischen `rw` und `rewrite` ist, dass das erste automatisch versucht,
anschliessend `rfl` anzuwenden. Bei `rewrite` musst du `rfl` explizit noch aufrufen."
Message (a : ℕ) (b : ℕ) (c : ℕ) (d : ℕ) (h₁ : c = d) (h₂ : a = b) (h₃ : a = d) : d = d =>
"Der Hauptunterschied zwischen `rw` und `rewrite` ist, dass das erste automatisch versucht,
anschliessend `rfl` anzuwenden. Bei `rewrite` musst du `rfl` explizit noch aufrufen."
Conclusion "Übrigens, mit `rw [h₁] at h₂` kann man auch eine andere Annahme umschreiben
anstatt dem Goal."
-- TODO: Das macht es doch unmöglich mit den Messages...
Tactics assumption
--Tactics rw