From d8643d22247dcabd39224aea8b88a30be0a66fc1 Mon Sep 17 00:00:00 2001 From: "joel.vonderwe" <joel.von-der-weid@etu.hesge.ch> Date: Thu, 27 Feb 2020 15:27:23 +0100 Subject: [PATCH] HK functions --- base_tp/src/hkfunc/hkfunc.scala | 73 +++++++++++++++++++++++++++++++++ base_tp/test/hkfuncTest.scala | 49 ++++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 base_tp/src/hkfunc/hkfunc.scala create mode 100644 base_tp/test/hkfuncTest.scala diff --git a/base_tp/src/hkfunc/hkfunc.scala b/base_tp/src/hkfunc/hkfunc.scala new file mode 100644 index 0000000..fdbceef --- /dev/null +++ b/base_tp/src/hkfunc/hkfunc.scala @@ -0,0 +1,73 @@ +package ch.hepia.tpscala + +/* Implémentez les fonctions suivantes. Vous ne pouvez utiliser que les + * méthode de 'List' vues dans les exercices précédents. + */ +object HKFunc { + + /* La fonction 'map' applique une fonction 'f' sur chaque élément de + * la liste 'as'. La liste résultat doit avoir la même longueur que + * l'argument. + */ + def map[A,B]( as: List[A] )( f: A=>B ): List[B] = { + def mapRec(as: List[A], news : List[B]) : List[B] = { + as match { + case Nil => news.reverse + case a :: rest => mapRec(rest, f(a) :: news) + } + } + mapRec(as, Nil) + } + + /* La fonction 'filter' utilise le prédicat 'f' pour déterminer quel + * élément garder. Le résultat peut être vide, mais l'ordre doit + * être préservé. + */ + def filter[A]( as: List[A] )( f: A=>Boolean ): List[A] = { + def filterRec(as: List[A], news : List[A]): List[A] = { + as match { + case Nil => news.reverse + case a :: rest if f(a) => filterRec(rest, a :: news) + case _ :: rest => filterRec(rest, news) + } + } + filterRec(as, Nil) + } + + /* Réduit une liste 'as' en utilisant une opération binaire 'f'. On + * supposera que 'as' n'est pas vide. + */ + def reduce[A]( as: List[A] )( f: (A,A)=>A ): A = { + def reduceRec(as: List[A], acc : A): A = { + as match { + case Nil => acc + case a :: rest => reduceRec(rest, f(acc, a)) + } + } + reduceRec(as.tail, as.head) + } + + /* Transforme une fonction 'f' en une fonction s'appliquant sur une + * liste. Utiliser la fonction 'map' définie ci-dessus + */ + def lift[A,B]( f: A=>B ): List[A]=>List[B] = { + (as: List[A]) => { + map(as)(f) + } + } + + /* DIFFICILE. Transforme une liste 'as' au moyen de la fonction 'f'. + * Cette fonction est appliquée à chaque élément de 'as' pour + * produire une nouvelle liste (qui peut être vide). Le résultat est + * la concaténation de chaque nouvelle liste en respectant l'ordre. + */ + def bind[A,B]( as: List[A] )( f: A=>List[B] ): List[B] = { + def bindRec(as: List[A], bs: List[B]): List[B] = { + as match { + case Nil => bs + case a :: rest => bindRec(rest, bs ++ f(a)) + } + } + bindRec(as, Nil) + } +} diff --git a/base_tp/test/hkfuncTest.scala b/base_tp/test/hkfuncTest.scala new file mode 100644 index 0000000..4e6e26b --- /dev/null +++ b/base_tp/test/hkfuncTest.scala @@ -0,0 +1,49 @@ +package ch.hepia.tpscala + + +import org.scalatest.funsuite.AnyFunSuite +import HKFunc._ + +class HKFunc5Suite extends AnyFunSuite { + + val i0 = List[Int]() + val is = List( 1, 2, 3, 4 ) + + test("map") { + assert( map( i0 )(_ + 1 ) == i0 ) + assert( map( is )(_ + 1 ) == List( 2, 3, 4, 5 ) ) + assert( map( is )(_ * 2 ) == List( 2, 4, 6, 8 ) ) + } + + test("filter") { + assert( filter( i0 )(_ % 2 == 0 ) == i0 ) + assert( filter( is )(_ % 2 == 0 ) == List( 2, 4 ) ) + } + + test("reduce") { + assert( reduce( is )( _ + _ ) == is.sum ) + assert( reduce( is )( _ * _ ) == 24 ) + } + + test("lift") { + val f = (_:Int) + 1 + val g = (_:Int) * 2 + val id = lift[Int,Int]( identity ) + assert( id(i0) == i0 ) + assert( id(is) == is ) + val h1 = lift( f andThen g ) + val h2 = lift(f) andThen lift(g) + assert( h1(i0) == h2(i0) ) + assert( h1(is) == h2(is) ) + } + + test("bind") { + val k = (i:Int) => if( i%2 == 0 ) List( -i, i ) else Nil + val l = (i:Int) => List( i, i ) + assert( bind( i0 )( k ) == i0 ) + assert( bind( is )( k ) == List( -2, 2, -4, 4 ) ) + assert( bind( is )( l ) == List( 1, 1, 2, 2, 3, 3, 4, 4 ) ) + } + + +} -- GitLab