简体   繁体   中英

Proof by induction with multiple lists

I am following the Functional Programming in Scala lecture on Coursera and at the end of the video 5.7, Martin Odersky asks to prove by induction the correctness of the following equation :

(xs ++ ys) map f = (xs map f) ++ (ys map f)

How to handle proof by induction when there are multiple lists involved ?

I have checked the base cases of xs being Nil and ys being Nil. I have proven by induction that the equation holds when xs is replaced by x::xs, but do we also need to check the equation with ys replaced by y::ys ?

And in that case (without spoiling the exercise too much...which is not graded anyway) how do you handle : (xs ++ (y::ys)) map f ?

This is the approach I have used on a similar example, to prove that

(xs ++ ys).reverse = ys.reverse ++ xs.reverse

Proof (omitting the base case, and easy x::xs case) :

(xs ++ (y::ys)).reverse
= (xs ++ (List(y) ++ ys)).reverse         //y::ys = List(y) ++ ys
= ((xs ++ List(y)) ++ ys).reverse         //concat associativity
= ys.reverse ++ (xs ++ List(y)).reverse   //by induction hypothesis (proven with x::xs)
= ys.reverse ++ List(y).reverse ++ xs.reverse //by induction hypothesis
= ys.reverse ++ (y::Nil).reverse ++ xs.reverse //List(y) = y :: Nil
= ys.reverse ++ Nil.reverse ++ List(y) ++ xs.reverse //reverse definition
= (ys.reverse ++ List(y)) ++ xs.reverse //reverse on Nil (base case)
= (y :: ys).reverse ++ xs.reverse         //reverse definition

Is this right ?

The property involves multiple lists, but ++ only recurses on its left argument. That's a hint that you can prove by induction on that left argument. In general, when proving a proposition about some recursive function, the first thing you try is inducting on the same argument that function recurses on .

I'll do this one for you as an example:

Claim : (xs ++ ys) map f = (xs map f) ++ (ys map f)

Proof : by induction on xs .

  • Base case: xs = Nil

    • lhs = (Nil ++ ys) map f = ys map f

      (by ++ 's definition)

    • rhs = (Nil map f) ++ (ys map f) = Nil ++ ys map f = ys map f

      (by map 's, then ++ 's definitions)

    • Hence lhs = rhs
  • Inductive case: xs = z :: zs

    • hypothesis : (zs ++ ys) map f = (zs map f) ++ (ys map f)
    • goal : ((z :: zs) ++ ys) map f = ((z :: zs) map f) ++ (ys map f)
    • lhs = (z :: (zs ++ ys)) map f = f(z) :: ((zs ++ ys) map f) (1)

      (by map 's definition)

    • rhs = ((z :: zs) map f) ++ (ys map f) = (f(z) :: (zs map f)) ++ (ys map f)

      (by map 's definition)

    • in turn, rhs = f(z) :: ((zs map f) ++ (ys map f)) (2)

      (by ++ 's definition)

    • From hypothesis , (1) and (2) , we have proven goal .

Therefore, we have proven the claim to be true reguardless of xs , ys , and f .

As the comment of @Phil says, first is a good understaning of what the methods ++ and :: are doing on the lists the better way is the documentation

How can we prove properties of list programs? The answer is by Structural induction! Proof rule for proving a list property P(xs) via structural induction:

P(Nil) (base case) for all x,xs : P(xs) => P(x::xs) (induction step)

for all xs : P(xs) (consequence)

P(xs) in induction step is called induction hypothesis

for as the only important thing is xs, ys is fix proper List with lenght l, after proving for xs you can proof for ys, or see that is commutative

So let's apply induction and the definitions of the functions

P(xs): (xs ++ ys) map f = (xs map f) ++ (ys map f)

Base case we substitue xs by nil

(nil ++ ys) map f [definition of ++ ] 
ys map f  on the other hand 
(xs map f) ++ (ys map p) [apply map over NIL] 
(NIL) ++ (ys map p) [definition pf ++] 
ys map p

Induction Step

((x::xs) ++ ys) map f [definition ++]
(x:: (xs ++ ys)) map f [definition map]
f(x) :: ((xs ++ ys) map f) [induction hypothesis]
f(x) :: ((xs map f) ++ (ys map f)) [definition ++]
(f(x) :: (xs map f)) ++ (ys map f) [definition map]
(x::xs) map f ++ ys map f

qed

for example another case in a scala work sheet

import scala.util.Random

// P : length ( append(as,bs) )) = length ( as ) + length (bs)

def length[T](as: List[T]): Int = as match {
    case Nil => 0
    case _::xs => 1 + length(xs)
}

def append[T](as: List[T], bs: List[T]): List[T] = as match {
  case Nil => bs
  case x :: xs => x :: append(xs, bs)
}

// base case  we substitute Nil for as in P

val a:List[Int] = Nil
val n = 10
val b:List[Int] = Seq.fill(n)(Random.nextInt).toList

length((append(a,b)))

length(a)

length(b)

import scala.util.Random

length: length[T](val as: List[T]) => Int




append: append[T](val as: List[T],val bs: List[T]) => List[T]






a: List[Int] = List()
n: Int = 10
b: List[Int] = List(1168053950, 922397949, -1884264936, 869558369, -165728826, -1052466354, -1696038881, 246666877, 1673332480, -975585734)

res0: Int = 10

res1: Int = 0

res2: Int = 10

here you can find more examples

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM