Built with doc-gen4, running Lean4. Bubbles () indicate interactive fragments: hover for details, tap to reveal contents. Use Ctrl+↑Ctrl+↓to navigate, Ctrl+πŸ–±οΈto focus. On Mac, use Cmdinstead of Ctrl.
import Mathlib

/-!
# Recursive functions

We have defined functions in terms of other functions. Besides this essentially the only way to define functions is _recursion_. In __Dependent Type Theory__ recursion/induction is very powerful.

In particular, unlike imperative programming all loops are implemented in terms of recursion.

The first example is the factorial function:
-/

/-- The factorial function -/
def 
fctrl: β„• β†’ β„•
fctrl
(n :
β„•: Type
β„•
):
β„•: Type
β„•
:= match n with | 0 =>
1: ?m.22
1
| n + 1 => (n +
1: ?m.109
1
) * (
fctrl: β„• β†’ β„•
fctrl
n) /-! We check by evaluating this #eval fctrl 5 -- 120 -/
120
fctrl: β„• β†’ β„•
fctrl
5: ?m.446
5
-- 120 /-! ## Fibonacci numbers: These are given by the recursive definition * `fib 0 = 1` * `fib 1 = 1` * `fib (n + 2) = fib n + (fib (n + 1))` -/ def
fib: β„• β†’ β„•
fib
:
β„•: Type
β„•
β†’
β„•: Type
β„•
| 0 =>
1: ?m.909
1
| 1 =>
1: ?m.923
1
| n + 2 =>
fib: β„• β†’ β„•
fib
n + (
fib: β„• β†’ β„•
fib
(n +
1: ?m.1009
1
)) /-! The above definition is not efficient as a single computation is called many times. We can instead define pairs, so if `(a, b) = (fib n, fib (n + 1))` then `(fib (n + 1), fib (n + 2)) = (b, a+ b)` To define Fibonacci pairs as above, we have two choices. We can view the pair at `n` as obtained by `n` iterations of the function `g: (a, b) ↦ (b, a + b)` starting with the pair `(1,1)`. Note that we can recursively use either `g^(n + 1) = g^n ∘ g` or `g^(n + 1) = g ∘ g^n`. The former is more efficient as it means the recursive function is called at the end to give a result, with modified arguments. This allows so called _tail call optimization_, where new copies of the function are not created. -/ def
fibAux: β„• β†’ β„• β†’ β„• β†’ β„• Γ— β„•
fibAux
(a b n :
β„•: Type
β„•
) :
β„•: Type
β„•
Γ—
β„•: Type
β„•
:= match n with | 0 => (a, b) | k + 1 =>
fibAux: β„• β†’ β„• β†’ β„• β†’ β„• Γ— β„•
fibAux
b (a + b) k def
fib': (n : ?m.1785) β†’ ?m.1789 n
fib'
(
n: ?m.1785
n
) := (
fibAux: β„• β†’ β„• β†’ β„• β†’ β„• Γ— β„•
fibAux
1: ?m.1793
1
1: ?m.1804
1
n: ?m.1785
n
).
1: {Ξ± : Type ?u.1807} β†’ {Ξ² : Type ?u.1806} β†’ Ξ± Γ— Ξ² β†’ Ξ±
1
433494437
fib': β„• β†’ β„•
fib'
42: ?m.1839
42
/-! The following definition is clearly wrong and will loop infinitely. Indeed if we attempt to define ```lean def silly_fib : β„• β†’ β„• | 0 => 1 | 1 => 1 | n + 2 => silly_fib n + (silly_fib (n + 3)) ``` we get the error message ```lean fail to show termination for silly_fib with errors argument #1 was not used for structural recursion failed to eliminate recursive application silly_fib (n + 3) structural recursion cannot be used failed to prove termination, use `termination_by` to specify a well-founded relation ``` -/ partial def
silly_fib: β„• β†’ β„•
silly_fib
:
β„•: Type
β„•
β†’
β„•: Type
β„•
| 0 =>
1: ?m.2300
1
| 1 =>
1: ?m.2314
1
| n + 2 =>
silly_fib: β„• β†’ β„•
silly_fib
n + (
silly_fib: β„• β†’ β„•
silly_fib
(n +
3: ?m.2400
3
)) /-! However even if functions terminate we can still get such an error. This is because Lean does not know that the function is terminating. We can fix this by adding the `partial` keyword but better still we can prove termination. Thus, the following definition is correct but gives an error. ```lean def hcf (a b : β„•) : β„• := if b < a then hcf b a else if a = 0 then b else hcf a (b - a) #eval hcf 18 12 -- 6 ``` -/ def
hcf: β„• β†’ β„• β†’ β„•
hcf
(a b :
β„•: Type
β„•
) :
β„•: Type
β„•
:= if
c: ?m.2681
c
:b < a then
hcf: β„• β†’ β„• β†’ β„•
hcf
b a else if
c'': ?m.2724
c''
:a =
0: ?m.2685
0
then b else have _ : b - a < b :=

Goals accomplished! πŸ™
a, b: β„•

c: Β¬b < a

c'': Β¬a = 0


b - a < b
a, b: β„•

c: Β¬b < a

c'': Β¬a = 0


hβ‚€
0 < a
a, b: β„•

c: Β¬b < a

c'': Β¬a = 0


h₁
a, b: β„•

c: Β¬b < a

c'': Β¬a = 0


b - a < b
a, b: β„•

c: Β¬b < a

c'': Β¬a = 0


hβ‚€
0 < a
a, b: β„•

c: Β¬b < a

c'': Β¬a = 0


h₁
a, b: β„•

c: Β¬b < a

c'': Β¬a = 0


hβ‚€.a
a β‰  0
a, b: β„•

c: Β¬b < a

c'': Β¬a = 0


hβ‚€
0 < a
a, b: β„•

c: Β¬b < a

c'': Β¬a = 0


h₁

Goals accomplished! πŸ™
a, b: β„•

c: Β¬b < a

c'': Β¬a = 0


b - a < b
a, b: β„•

c: Β¬b < a

c'': Β¬a = 0


h₁
a, b: β„•

c: Β¬b < a

c'': Β¬a = 0


h₁.a
Β¬b < a
a, b: β„•

c: Β¬b < a

c'': Β¬a = 0


h₁

Goals accomplished! πŸ™
hcf: β„• β†’ β„• β†’ β„•
hcf
a (b - a) termination_by _ a b => (a, b) /-! ```lean partial def wrong(n: β„•): Empty := wrong (n + 1) ``` gives the error message ```lean failed to compile partial definition 'wrong', failed to show that type is inhabited and non empty ``` -/ -- partial def wrong(n: β„•): Empty := -- wrong (n + 1)
6
hcf: β„• β†’ β„• β†’ β„•
hcf
18: ?m.39427
18
12: ?m.39437
12
-- 6
Nat.pos_of_ne_zero {n : β„•} (a✝ : n β‰  0) : 0 < n
Nat.pos_of_ne_zero: βˆ€ {n : β„•}, n β‰  0 β†’ 0 < n
Nat.pos_of_ne_zero
Nat.sub_lt_of_pos_le (a b : β„•) (hβ‚€ : 0 < a) (h₁ : a ≀ b) : b - a < b
Nat.sub_lt_of_pos_le: βˆ€ (a b : β„•), 0 < a β†’ a ≀ b β†’ b - a < b
Nat.sub_lt_of_pos_le
/-! Lean has to allow partial definitions due to deep results of Church-GΓΆdel-Turing-..., which say for example that we cannot prove that a Lean interpreter in Lean terminates. -/