Skip to content

Commit db3feb5

Browse files
committed
Added P02S03 until B02
1 parent c67ea7a commit db3feb5

1 file changed

Lines changed: 314 additions & 0 deletions

File tree

LeanBlockCourse26/P02_Logic/S03_Connectives.lean

Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,3 +254,317 @@ example (P Q R : Prop) (h₁ : P → Q) (h₂ : P → R) : P → (Q ∧ R) := by
254254
-- ... and finally get a simple term proof.
255255
example (P Q R : Prop) (h₁ : P → Q) (h₂ : P → R) : P → (Q ∧ R) :=
256256
fun p => ⟨h₁ p, h₂ p⟩
257+
258+
/-
259+
## Intermission: The `repeat`, `all_goals`, `try`, and `<;>` tactics
260+
261+
- `repeat tac` repeatedly applies `tac` to the main goals until it fails.
262+
- `all_goals tac` runs `tac` on each goal, concatenating the resulting goals, if any.
263+
- `try tac` attempts to run `tac` without causing failure if it does not apply.
264+
- `tac <;> tac'` runs `tac` on the main goal and `tac'` on each produced goal.
265+
266+
They are respectively used around 150, 500, 400, and 7000 times in mathlib.
267+
-/
268+
269+
-- We have seen this example before ...
270+
example (P Q : Prop) (h : P ∧ Q) : Q ∧ P := by
271+
cases h -- or `obtain ⟨p, q⟩ := h` or `rcases h with ⟨p, q⟩`
272+
constructor
273+
· assumption
274+
· assumption
275+
276+
-- ... but now we can do it more compactly with `repeat` ...
277+
example (P Q : Prop) (h : P ∧ Q) : Q ∧ P := by
278+
cases h
279+
constructor
280+
repeat assumption
281+
282+
-- ... or alternatively with `all_goals` ...
283+
example (P Q : Prop) (h : P ∧ Q) : Q ∧ P := by
284+
cases h
285+
constructor
286+
all_goals assumption
287+
288+
-- ... or with `<;>`
289+
example (P Q : Prop) (h : P ∧ Q) : Q ∧ P := by
290+
cases h
291+
constructor <;> assumption
292+
293+
-- We can also just `try` to execute a tactic.
294+
example (P Q : Prop) (h : P ∧ Q) : Q ∧ P := by
295+
obtain ⟨p, q⟩ := h
296+
constructor
297+
all_goals -- This is needed since otherwise `try exact p` would only try to match goal 1
298+
try exact p -- Here the `try` is required ...
299+
try exact q -- ... and here of course the `try` is superfluous,
300+
301+
-- Testing the boundaries
302+
303+
example (P Q : Prop) (h : P ∧ Q) : Q ∧ P := by
304+
obtain ⟨p, q⟩ := h
305+
constructor
306+
repeat exact q -- correctly applies to first goal
307+
exact p
308+
309+
example (P Q : Prop) (h : P ∧ Q) : Q ∧ P := by
310+
obtain ⟨p, q⟩ := h
311+
constructor
312+
repeat exact p -- works technically but doesn't actually do anything (linter complains)
313+
exact q
314+
exact p
315+
316+
-- This fails: `all_goals` *actually* applies, *repeat* just tried to apply and stops
317+
-- example (P Q : Prop) (h : P ∧ Q) : Q ∧ P := by
318+
-- obtain ⟨p, q⟩ := h
319+
-- constructor
320+
-- all_goals
321+
-- exact q
322+
-- exact p
323+
324+
-- For the same reason this fails:
325+
-- example (P Q : Prop) (h : P ∧ Q) : Q ∧ P := by
326+
-- obtain ⟨p, q⟩ := h
327+
-- constructor <;> exact p
328+
-- exact q
329+
330+
-- So you need `try` in both the `all_goals` ...
331+
example (P Q : Prop) (h : P ∧ Q) : Q ∧ P := by
332+
obtain ⟨p, q⟩ := h
333+
constructor
334+
all_goals
335+
try exact q
336+
exact p
337+
338+
-- ... and the `<;>`
339+
example (P Q : Prop) (h : P ∧ Q) : Q ∧ P := by
340+
obtain ⟨p, q⟩ := h
341+
constructor <;> try exact p
342+
exact q
343+
344+
/-
345+
Basically: chained `<;>` is the same as an indented `all_goals` block.
346+
347+
* `all_goals` is parallel but fails if something does not fit the expected type
348+
* `repeat` is sequential and stops if something does not fit the expected type
349+
* `all_goals` combined with `try` is parallel and does not fail
350+
-/
351+
352+
/-
353+
## Working with OR (∨) in the goal
354+
355+
To prove P ∨ Q, we need to prove either P or Q. We can:
356+
357+
- Use `apply Or.inl`/`Or.inr` explicitly
358+
- Use `left`/`right` as shorthand
359+
-/
360+
361+
-- The most explicit way to deal with `∨` in goal is to
362+
-- directly use `apply Or.inl` or `apply Or.inr`
363+
theorem goal_or_apply (P Q : Prop) (p : P) : P ∨ Q := by
364+
apply Or.inl
365+
exact p
366+
367+
#print goal_or_apply -- gives `Or.inl p`
368+
369+
-- Again note that `apply` is destructive since `apply Or.inr` here
370+
-- would have left us with a goal that cannot be proven from the assumptions.
371+
-- example (P Q : Prop) (p : P) : P ∨ Q := by
372+
-- apply Or.inr
373+
-- ... now we are stuck
374+
375+
-- But we could have argued forward here ..
376+
theorem goal_or_exact (P Q : Prop) (p : P) : P ∨ Q := by
377+
exact Or.inl p
378+
379+
#print goal_or_exact -- also gives `Or.inl p`
380+
381+
-- .. which also gives the term mode proof.
382+
theorem goal_or_term (P Q : Prop) (p : P) : P ∨ Q := Or.inl p
383+
384+
#print goal_or_term -- also gives `Or.inl p`
385+
386+
-- Perhaps more intuitive are the `left` and `right` tactics
387+
theorem goal_or_tactic (P Q : Prop) (p : P) : P ∨ Q := by
388+
left
389+
exact p
390+
391+
#print goal_or_tactic -- also gives `Or.inl p`
392+
393+
/-
394+
## Working with OR in a hypothesis
395+
396+
To use `h : P ∨ Q`, we can:
397+
- Use `apply Or.elim` explicitly
398+
- Use `cases` and `rcases`
399+
- Use `obtain` with pattern matching
400+
-/
401+
402+
-- We can deal with `∨` in a hypethesis by applying `Or.elim` directly,
403+
-- again using `·` to structure the proof to the sub-goals. Note that
404+
-- `Or.elim {...} (h : a ∨ b) (left : a → c) (right : b → c) : c`
405+
406+
-- Viewing `Or.elim` as a method, the most obvious thing to do is ...
407+
example (P Q R : Prop) (h : P ∨ Q) (p_to_r : P → R) (q_to_r : Q → R) : R := by
408+
exact Or.elim h p_to_r q_to_r
409+
410+
-- ... or even just use term mode.
411+
example (P Q R : Prop) (h : P ∨ Q) (p_to_r : P → R) (q_to_r : Q → R) : R :=
412+
Or.elim h p_to_r q_to_r
413+
414+
-- But if we want to get towards what we naturally expect, a case distinction,
415+
-- we need to use `apply` ...
416+
example (P Q R : Prop) (h : P ∨ Q) (p_to_r : P → R) (q_to_r : Q → R) : R := by
417+
apply Or.elim h
418+
· exact p_to_r -- Note that you do not have `p : P` in the assumptions here ...
419+
· exact q_to_r -- ... and likewise you do not have `q : Q` here.
420+
421+
-- ... but if you really want a case distinction as you expect it, you need.
422+
example (P Q R : Prop) (h : P ∨ Q) (p_to_r : P → R) (q_to_r : Q → R) : R := by
423+
apply Or.elim h
424+
· intro p
425+
exact p_to_r p
426+
· intro q
427+
exact q_to_r q
428+
429+
-- Note that `apply` just looks for the output of the applied statement in the
430+
-- goal and makes you prove all the assumptions of the applied statement, so
431+
-- if we did not do the partial application `Or.elim h`, we would have gotten
432+
-- three subgoals, since `Or.elim` takes three arguments.
433+
example (P Q R : Prop) (h : P ∨ Q) (p_to_r : P → R) (q_to_r : Q → R) : R := by
434+
apply Or.elim -- no `h` here
435+
· exact h
436+
· exact p_to_r
437+
· exact q_to_r
438+
439+
/-
440+
This show why tactis are good to have: you do not need to remember `Or.elim``
441+
or how exactly it is structured. You just use `cases`, `rcases`, or `cases'`
442+
and get exactly the number of cases in the case distinction that you would expect.
443+
-/
444+
445+
-- We can use the `cases` tactic to do a case distinction on a hypothesis ...
446+
example (P Q R : Prop) (h : P ∨ Q) (p_to_r : P → R) (q_to_r : Q → R) : R := by
447+
cases h
448+
· exact p_to_r (by assumption)
449+
· exact q_to_r (by assumption)
450+
451+
-- ... and if we want named variables we can also do proper pattern matching
452+
example (P Q R : Prop) (h : P ∨ Q) (p_to_r : P → R) (q_to_r : Q → R) : R := by
453+
cases h with
454+
| inl p => exact p_to_r p
455+
| inr q => exact q_to_r q
456+
457+
-- But most likely you should just use `rcases with _ | _` ...
458+
example (P Q R : Prop) (h : P ∨ Q) (p_to_r : P → R) (q_to_r : Q → R) : R := by
459+
rcases h with p | q -- compare to previous `rcases h with ⟨p, q⟩`
460+
· exact p_to_r p
461+
· exact q_to_r q
462+
463+
-- ... or you can use `obatain _ | _ := ...`
464+
example (P Q R : Prop) (h : P ∨ Q) (p_to_r : P → R) (q_to_r : Q → R) : R := by
465+
obtain p | q := h -- compare to previous `obtain ⟨p, q⟩ := h`
466+
· exact p_to_r p
467+
· exact q_to_r q
468+
469+
-- Note that `cases'` is likewise marked as deprecated by the linter.
470+
example (P Q R : Prop) (h : P ∨ Q) (p_to_r : P → R) (q_to_r : Q → R) : R := by
471+
cases' h with p q
472+
· exact p_to_r p
473+
· exact q_to_r q
474+
475+
/-
476+
## Working with nested structures
477+
478+
For more complex structures, we can:
479+
- Use `rcases` for deep pattern matching
480+
- Use `obtain` with nested patterns
481+
-/
482+
483+
-- This is the brute force way ...
484+
example (P Q R : Prop) (h : P ∧ Q ∧ R) : Q := by
485+
obtain ⟨_, qr⟩ := h
486+
obtain ⟨q, _⟩ := qr
487+
exact q
488+
489+
-- ... but even with what we have seen there is a nicer (term mode) proof.
490+
example (P Q R : Prop) (h : P ∧ Q ∧ R) : Q :=
491+
h.right.left -- or `h.2.1`
492+
493+
-- But we can also do the deconstruction of `h` in the assumptions more cleanly:
494+
example (P Q R : Prop) (h : P ∧ Q ∧ R) : Q := by
495+
obtain ⟨_, ⟨q, _⟩⟩ := h
496+
exact q
497+
498+
-- We can even get rid of the nested brackets ...
499+
example (P Q R : Prop) (h : P ∧ Q ∧ R) : Q := by
500+
obtain ⟨_, q, _⟩ := h
501+
exact q
502+
503+
-- ... but only because `P ∧ Q ∧ R` was bracketed the "natural" way.
504+
example (P Q R : Prop) (h : (P ∧ Q) ∧ R) : Q := by
505+
obtain ⟨⟨_, q⟩, _⟩ := h -- here `⟨_, q, _⟩` does not work because of `(P ∧ Q) ∧ R`
506+
exact q
507+
508+
-- Nested patterns also work with `rcases`.
509+
example (P Q R : Prop) (h : P ∧ Q ∧ R) : Q := by
510+
rcases h with ⟨_, q, _⟩
511+
exact q
512+
513+
514+
/-
515+
## The `rintro` tactic
516+
517+
`rintro` allows for more complex pattern matching and is
518+
used around 7500 times in mathlib.
519+
-/
520+
521+
-- Mixing `∧` with `∨` can quickly becomes very annoying ...
522+
example (P Q R : Prop) : (P ∧ Q) ∨ R → P ∨ R := by
523+
intro h
524+
rcases h with pq | r
525+
· obtain ⟨p, q⟩ := pq
526+
left
527+
exact p
528+
· right
529+
exact r
530+
531+
-- ... but we can also do mixed nested patterns with `rcases` ...
532+
example (P Q R : Prop) : (P ∧ Q) ∨ R → P ∨ R := by
533+
intro h
534+
rcases h with ⟨p, q⟩ | r
535+
· left
536+
exact p
537+
· right
538+
exact r
539+
540+
-- ... or with `obtain`
541+
example (P Q R : Prop) : (P ∧ Q) ∨ R → P ∨ R := by
542+
intro h
543+
obtain ⟨p, q⟩ | r := h
544+
· left
545+
exact p
546+
· right
547+
exact r
548+
549+
-- But if we also want to do the pattern matching in the
550+
-- `intro` (like we have previously seen) we now need `rintro`.
551+
example (P Q R : Prop) : (P ∧ Q) ∨ R → P ∨ R := by
552+
rintro (⟨p, q⟩ | r)
553+
· left
554+
exact p
555+
· right
556+
exact r
557+
558+
/-
559+
# Exercise Block B02
560+
561+
Hint: try `rintro` with nested structures
562+
-/
563+
564+
-- Exercise 2.1
565+
example (P Q R S : Prop) : (P ∨ Q) ∧ (R ∨ S) → (P ∧ R) ∨ (P ∧ S) ∨ (Q ∧ R) ∨ (Q ∧ S) := by
566+
sorry
567+
568+
-- Exercise 2.2
569+
example (P Q R S : Prop) : ((P ∧ Q) ∨ R) ∧ S → (P ∨ R) ∧ (Q ∨ R) ∧ S := by
570+
sorry

0 commit comments

Comments
 (0)