Zulip Chat Archive
Stream: new members
Topic: Creating a case analysis lemma for a function
pandaman (Apr 29 2025 at 05:24):
Hi! I'd like to create a lemma to abstract the case analysis of a (non-recursive) function so that I can easily perform the same case analysis in different results (ideally using the cases
tactic). For recursive functions, we now have auto-generated .induct
, so mimicking the signature allowed me to use the induction
tactic with my own induction principle. However, I'm lost how to do something like this for a non-recursive function. Any advice are welcome!
set_option autoImplicit false
inductive Node where
| done
| epsilon (next : Nat)
def pushNext (n : Node) (stack : List Nat) : List Nat :=
match n with
| .done => stack
| .epsilon next =>
if next < 10 then
next :: stack
else
stack
-- A case analysis lemma
theorem pushNext.cases (n : Node) (stack : List Nat)
(motive : List Nat → Prop)
(done : n = .done → motive stack)
(epsilon_pos : ∀ (next : Nat), n = .epsilon next → next < 10 → motive (next :: stack))
(epsilon_neg : ∀ (next : Nat), n = .epsilon next → ¬next < 10 → motive stack) :
motive (pushNext n stack) := by
unfold pushNext
split
next n => exact done rfl
next n next =>
split
next lt => exact epsilon_pos next rfl lt
next nlt => exact epsilon_neg next rfl nlt
example (n : Node) (stack : List Nat) (h : ∀ i ∈ stack, i < 10) : ∀ i ∈ pushNext n stack, i < 10 := by
apply pushNext.cases (motive := fun stack => ∀ i ∈ stack, i < 10)
-- Cannot introduce variables due to "too many variable names provided"
case done /-h-/ => sorry
case epsilon_pos /-next hn lt-/ => sorry
case epsilon_neg /-next hn nlt-/ => sorry
example (n : Node) (stack : List Nat) (h : ∀ i ∈ stack, i < 10) : ∀ i ∈ pushNext n stack, i < 10 := by
-- How can I use `cases` here?
-- cases n, stack using pushNext.cases
sorry
Yaël Dillies (Apr 29 2025 at 06:14):
You should do induction on n : Node
, not on pushNext n stack
pandaman (Apr 29 2025 at 06:30):
That means I need to add if h : next < 10
to the epsilon case every time, which I want to avoid.
pandaman (Apr 29 2025 at 06:34):
Functional induction does case analysis (along the branching of a function) + induction. I want the former for non-recursive functions.
Markus Himmel (Apr 29 2025 at 06:44):
Are you aware of the fun_cases
tactic, i.e., fun_cases pushNext n stack
?
Markus Himmel (Apr 29 2025 at 06:46):
Your pushNext.cases
lemma is quite similar to the autogenerated pushNext.fun_cases
(which is created by the same machinery as the .induct
lemmas for recursive functions). The fun_cases
tactic applies that lemma for you (just like the fun_induction
tactic applies the .induct
lemma for you).
pandaman (Apr 29 2025 at 06:46):
I'll try it. Thank you!
pandaman (Apr 29 2025 at 07:48):
It worked perfectly. Thank you! I missed we have .fun_cases
(I tried .cases
and .casesOn
and assumed it's not implemented :sweat_smile: )
pandaman (Apr 29 2025 at 07:56):
Now I have something like this
set_option autoImplicit false
inductive Node where
| done
| epsilon (next : Nat)
def pushNext (n : Node) (stack : List Nat) : List Nat :=
match n with
| .done => stack
| .epsilon next =>
if next < 10 then
next :: stack
else
stack
-- Renaming branches
theorem pushNext.fun_cases' (motive : Node → List Nat → Prop)
(done : ∀ (stack : List Nat), motive Node.done stack)
(epsilon_pos : ∀ (stack : List Nat) (next : Nat), next < 10 → motive (Node.epsilon next) stack)
(epsilon_neg : ∀ (stack : List Nat) (next : Nat), ¬next < 10 → motive (Node.epsilon next) stack) :
∀ (n : Node) (stack : List Nat), motive n stack :=
pushNext.fun_cases motive done epsilon_pos epsilon_neg
example (n : Node) (stack : List Nat) (h : ∀ i ∈ stack, i < 10) : ∀ i ∈ pushNext n stack, i < 10 := by
cases n, stack using pushNext.fun_cases'
case done =>
simpa [pushNext] using h
case epsilon_pos next lt =>
simpa [pushNext, lt] using h
case epsilon_neg next nlt =>
simpa [pushNext, nlt] using h
Last updated: May 02 2025 at 03:31 UTC