Skip to main content

Proofs of the equality length(xs ++ ys) = length(xs) + length(ys)

In Lean, the functions

    length : List α  Nat
    (++)   : List α  List α  List α

are defined such that

  • (length xs) is the length of xs. For example,

    length [2,3,5,3] = 4
    
  • (xs ++ ys) is the list obtained by concatenating xs and ys. For example.

    [1,2] ++ [2,3,5,3] = [1,2,2,3,5,3]
    

These functions are characterized by the following lemmas:

length_nil  : length [] = 0
length_cons : length (x :: xs) = length xs + 1
nil_append  : [] ++ ys = ys
cons_append : (x :: xs) ++ y = x :: (xs ++ ys)

To prove that

length (xs ++ ys) = length xs + length ys

To that end, complete the following Lean4 theory

import Mathlib.Tactic
open List

variable {α : Type}
variable (xs ys : List α)

example :
  length (xs ++ ys) = length xs + length ys :=
by sorry

1. Proof in natural language

By induction on xs.

Base case: Suppose that xs = []. Then,

length (xs ++ ys) = length ([] ++ ys)
                  = length ys
                  = 0 + length ys
                  = length [] + length ys
                  = length xs + length ys

Induction step: Suppose that xs = a :: as and that the induction hypothesis (IH) holds

length (as ++ ys) = length as + length ys                     (IH)

Then,

length (xs ++ ys) = length ((a :: as) ++ ys)
                  = length (a :: (as ++ ys))
                  = length (as ++ ys) + 1
                  = (length as + length ys) + 1      [por IH]
                  = (length as + 1) + length ys
                  = length (a :: as) + length ys
                  = length xs + length ys

2. Proofs with Lean4

import Mathlib.Tactic

open List

variable {α : Type}
variable (xs ys : List α)

-- Proof 1
-- =======

example :
  length (xs ++ ys) = length xs + length ys :=
by
  induction' xs with a as IH
  . calc length ([] ++ ys)
         = length ys
           := congr_arg length (nil_append ys)
       _ = 0 + length ys
           := (zero_add (length ys)).symm
       _ = length [] + length ys
           := congrArg (. + length ys) length_nil.symm
  . calc length ((a :: as) ++ ys)
         = length (a :: (as ++ ys))
           := congr_arg length (cons_append a as ys)
       _ = length (as ++ ys) + 1
           := length_cons a (as ++ ys)
       _ = (length as + length ys) + 1
           := congrArg (. + 1) IH
       _ = (length as + 1) + length ys
           := (Nat.succ_add (length as) (length ys)).symm
       _ = length (a :: as) + length ys
           := congrArg (. + length ys) (length_cons a as).symm

-- Proof 2
-- =======

example :
  length (xs ++ ys) = length xs + length ys :=
by
  induction' xs with a as IH
  . calc length ([] ++ ys)
         = length ys
           := by rw [nil_append]
       _ = 0 + length ys
           := (zero_add (length ys)).symm
       _ = length [] + length ys
           := by rw [length_nil]
  . calc length ((a :: as) ++ ys)
         = length (a :: (as ++ ys))
           := by rw [cons_append]
       _ = length (as ++ ys) + 1
           := by rw [length_cons]
       _ = (length as + length ys) + 1
           := by rw [IH]
       _ = (length as + 1) + length ys
           := (Nat.succ_add (length as) (length ys)).symm
       _ = length (a :: as) + length ys
           := congrArg (. + length ys) (length_cons a as).symm

-- Proof 3
-- =======

example :
  length (xs ++ ys) = length xs + length ys :=
by
  induction' xs with _ as IH
  . simp only [nil_append, zero_add, length_nil]
  . simp only [cons_append, length_cons, IH, Nat.succ_add]

-- Proof 4
-- =======

example :
  length (xs ++ ys) = length xs + length ys :=
by
  induction' xs with _ as IH
  . simp
  . simp [IH, Nat.succ_add]

-- Proof 5
-- =======

example :
  length (xs ++ ys) = length xs + length ys :=
by
  induction' xs with a as IH
  . simp
  . simp [IH] ; linarith

-- Proof 6
-- =======

lemma length_conc_1 (xs ys : List α):
  length (xs ++ ys) = length xs + length ys :=
by
  induction xs with
  | nil => calc
    length ([] ++ ys)
        = length ys
          := by rw [nil_append]
      _ = 0 + length ys
          := by rw [zero_add]
      _ = length [] + length ys
          := by rw [length_nil]
  | cons a as IH => calc
    length ((a :: as) ++ ys)
        = length (a :: (as ++ ys))
          := by rw [cons_append]
      _ = length (as ++ ys) + 1
          := by rw [length_cons]
      _ = (length as + length ys) + 1
          := by rw [IH]
      _ = (length as + 1) + length ys
          := (Nat.succ_add (length as) (length ys)).symm
      _ = length (a :: as) + length ys
          := by rw [length_cons]

-- Proof 7
-- =======

lemma length_conc_2 (xs ys : List α):
  length (xs ++ ys) = length xs + length ys :=
by
  induction xs with
  | nil          => simp
  | cons _ as IH => simp [IH, Nat.succ_add]

-- Proof 8
-- =======

example :
  length (xs ++ ys) = length xs + length ys :=
by
  induction' xs with a as IH
  . -- ⊢ length ([] ++ ys) = length [] + length ys
    rw [nil_append]
    -- ⊢ length ys = length [] + length ys
    rw [length_nil]
    -- ⊢ length ys = 0 + length ys
    rw [zero_add]
  . -- a : α
    -- as : List α
    -- IH : length (as ++ ys) = length as + length ys
    -- ⊢ length (a :: as ++ ys) = length (a :: as) + length ys
    rw [cons_append]
    -- ⊢ length (a :: (as ++ ys)) = length (a :: as) + length ys
    rw [length_cons]
    -- ⊢ Nat.succ (length (as ++ ys)) = length (a :: as) + length ys
    rw [IH]
    -- ⊢ Nat.succ (length as + length ys) = length (a :: as) + length ys
    rw [length_cons]
    -- ⊢ Nat.succ (length as + length ys) = Nat.succ (length as) + length ys
    rw [Nat.succ_add]

-- Proof 9
-- =======

example :
  length (xs ++ ys) = length xs + length ys :=
by
  induction' xs with a as IH
  . -- ⊢ length ([] ++ ys) = length [] + length ys
    rw [nil_append]
    -- ⊢ length ys = length [] + length ys
    rw [length_nil]
    -- ⊢ length ys = 0 + length ys
    rw [zero_add]
  . -- a : α
    -- as : List α
    -- IH : length (as ++ ys) = length as + length ys
    -- ⊢ length (a :: as ++ ys) = length (a :: as) + length ys
    rw [cons_append]
    -- ⊢ length (a :: (as ++ ys)) = length (a :: as) + length ys
    rw [length_cons]
    -- ⊢ Nat.succ (length (as ++ ys)) = length (a :: as) + length ys
    rw [IH]
    -- ⊢ Nat.succ (length as + length ys) = length (a :: as) + length ys
    rw [length_cons]
    -- ⊢ Nat.succ (length as + length ys) = length (a :: as) + length ys
    rw [Nat.succ_add]

-- Proof 10
-- ========

example :
  length (xs ++ ys) = length xs + length ys :=
by
  induction' xs with a as IH
  . -- ⊢ length ([] ++ ys) = length [] + length ys
    rw [nil_append]
    -- ⊢ length ys = length [] + length ys
    rw [length_nil]
    -- ⊢ length ys = 0 + length ys
    rw [zero_add]
  . -- a : α
    -- as : List α
    -- IH : length (as ++ ys) = length as + length ys
    -- ⊢ length (a :: as ++ ys) = length (a :: as) + length ys
    rw [cons_append]
    -- ⊢ length (a :: (as ++ ys)) = length (a :: as) + length ys
    rw [length_cons]
    -- ⊢ Nat.succ (length (as ++ ys)) = length (a :: as) + length ys
    rw [IH]
    -- ⊢ Nat.succ (length as + length ys) = length (a :: as) + length ys
    rw [length_cons]
    -- ⊢ Nat.succ (length as + length ys) = Nat.succ (length as) + length ys
    linarith

-- Proof 11
-- ========

example :
  length (xs ++ ys) = length xs + length ys :=
length_append xs ys

-- Proof 12
-- ========

example :
  length (xs ++ ys) = length xs + length ys :=
by simp

-- Used lemmas
-- ===========

-- variable (m n p : ℕ)
-- variable (x : α)
-- #check (Nat.succ_add n m : Nat.succ n + m = Nat.succ (n + m))
-- #check (cons_append x xs ys : (x :: xs) ++ ys = x :: (xs ++ ys))
-- #check (length_append xs ys : length (xs ++ ys) = length xs + length ys)
-- #check (length_cons x xs : length (x :: xs) = length xs + 1)
-- #check (length_nil : length [] = 0)
-- #check (nil_append xs : [] ++ xs = xs)
-- #check (zero_add n : 0 + n = n)

You can interact with the previous proofs at Lean 4 Web.

3. Proofs with Isabelle/HOL

theory "Proofs_of_the_equality_length(xs++ys)_Eq_length(xs)+length(ys)"
imports Main
begin

(* Proof 1 *)

lemma "length (xs @ ys) = length xs + length ys"
proof (induct xs)
  have "length ([] @ ys) = length ys"
    by (simp only: append_Nil)
  also have "\<dots> = 0 + length ys"
    by (rule add_0 [symmetric])
  also have "\<dots> = length [] + length ys"
    by (simp only: list.size(3))
  finally show "length ([] @ ys) = length [] + length ys"
    by this
next
  fix x xs
  assume HI : "length (xs @ ys) = length xs + length ys"
  have "length ((x # xs) @ ys) = length (x # (xs @ ys))"
    by (simp only: append_Cons)
  also have "\<dots> = length (xs @ ys) + 1"
    by (simp only: list.size(4))
  also have "\<dots> = (length xs + length ys) + 1"
    by (simp only: HI)
  also have "\<dots> = (length xs + 1) + length ys"
    by (simp only: add.assoc add.commute)
  also have "\<dots> = length (x # xs) + length ys"
    by (simp only: list.size(4))
  then show "length ((x # xs) @ ys) = length (x # xs) + length ys"
    by simp
qed

(* Proof 2 *)

lemma "length (xs @ ys) = length xs + length ys"
proof (induct xs)
  show "length ([] @ ys) = length [] + length ys"
    by simp
next
  fix x xs
  assume "length (xs @ ys) = length xs + length ys"
  then show "length ((x # xs) @ ys) = length (x # xs) + length ys"
    by simp
qed

(* Proof 3 *)

lemma "length (xs @ ys) = length xs + length ys"
proof (induct xs)
  case Nil
  then show ?case by simp
next
  case (Cons a xs)
  then show ?case by simp
qed

(* Proof 4 *)

lemma "length (xs @ ys) = length xs + length ys"
by (induct xs) simp_all

(* Proof 5 *)

lemma "length (xs @ ys) = length xs + length ys"
by (fact length_append)

(* Note: Auto_solve suggests the previous proof. *)

end

Note: The code for the previous proofs can be found in the Calculemus repository on GitHub.