From 6a84cda22b57e0a40ff3f0690d5fd1f646296f67 Mon Sep 17 00:00:00 2001
From: "joel.vonderwe" <joel.von-der-weid@etu.hesge.ch>
Date: Thu, 26 Mar 2020 16:00:23 +0100
Subject: [PATCH] Monoid exos

---
 base_tp/src/monoid/monoids.scala | 132 +++++++++++++++++++++++++++++++
 base_tp/test/8.monoidTest.scala  |  63 +++++++++++++++
 2 files changed, 195 insertions(+)
 create mode 100644 base_tp/src/monoid/monoids.scala
 create mode 100644 base_tp/test/8.monoidTest.scala

diff --git a/base_tp/src/monoid/monoids.scala b/base_tp/src/monoid/monoids.scala
new file mode 100644
index 0000000..7cb87b2
--- /dev/null
+++ b/base_tp/src/monoid/monoids.scala
@@ -0,0 +1,132 @@
+package ch.hepia.tpscala.monoid
+
+trait Monoid[A] {
+  def op( a1: A, a2: A ): A
+  def zero: A
+}
+
+object Monoid {
+  def apply[A]( z: A )( f: (A,A)=>A ) = new Monoid[A] {
+    def op( a1: A, a2: A ): A = f(a1,a2)
+    def zero: A = z
+  }
+
+  object Laws {
+    def checkAssociativity[A](
+      mon: Monoid[A], a1: A, a2: A, a3: A
+    ) = {
+      import mon.op
+      op(a1,op(a2,a3)) == op(op(a1,a2),a3)
+    }
+    def checkNeutral[A](
+      mon: Monoid[A], a: A
+    ) = {
+      import mon._
+      op(zero,a) == a && op(a,zero) == a
+    }
+  }
+}
+
+object Monoids {
+
+  val intSum = Monoid(0)( _ + _ )
+  val intProd = Monoid(1)( _ * _ )
+
+  val doubleSum = Monoid( 0.0 )( _ + _ )
+  val doubleMax = Monoid( Double.MinValue )( _ max _ )
+
+  def listFlat[A] = Monoid[List[A]]( Nil )( _ ++ _ )
+
+  def setUnion[A] = Monoid( Set.empty[A] )( _ ++ _ )
+
+  def pipeline[A] = Monoid[A=>A]( identity )( _ andThen _ )
+
+  def optionM[A]( op: (A,A)=>A ) =
+    Monoid[Option[A]]( None ){ (lhs,rhs) =>
+      (lhs,rhs) match {
+        case (None,_) => rhs
+        case (_,None) => lhs
+        case (Some(a),Some(b)) => Some( op(a,b) )
+      }
+    }
+
+  val optIntProd = optionM( intProd.op )
+  val optDoubleMax = optionM( doubleMax.op )
+
+
+  def tuple2M[A,B]( MA: Monoid[A], MB: Monoid[B] ) =
+    Monoid[(A,B)]( (MA.zero,MB.zero) ){ (ab1,ab2) =>
+      ( MA.op( ab1._1, ab2._1 ), MB.op( ab1._2, ab2._2 ) )
+    }
+
+
+  def mapM[K,V]( M: Monoid[V] ) = {
+    def merge( lhs: Map[K,V], rhs: Map[K,V] ): Map[K,V] = {
+      val ks = lhs.keySet ++ rhs.keySet
+      ks.map{ k =>
+        val v = M.op(
+          lhs.getOrElse(k,M.zero),
+          rhs.getOrElse(k,M.zero) )
+        k -> v
+      }.toMap
+    }
+    Monoid[Map[K,V]]( Map() )( merge )
+  }
+}
+
+object Examples {
+
+  import Monoids._
+
+  def mapReduce[A,B]( as: List[A] )(f: A=>B, M: Monoid[B]): B = {
+    def rec( rem: List[A], acc: B ): B = rem match {
+      case Nil => acc
+      case h :: t => rec( t, M.op(acc, f(h) ) )
+    }
+    rec( as, M.zero )
+  }
+  def reduce[A]( as: List[A] )( M: Monoid[A] ): A =
+    mapReduce( as )( identity, M )
+
+  //Donné à titre d'exemple
+  def averageM[A]( MA: Monoid[A] ): Monoid[(A,Int)] =
+    tuple2M( MA, intSum )
+  val avgDouble = averageM( doubleSum )
+  def average( as: List[Double] ): Option[Double] =
+    if( as.isEmpty ) None
+    else {
+      val (sum,n) =
+        mapReduce( as )( a => (a,1), avgDouble )
+      Some( sum/n )
+    }
+
+  //Série 3: and
+  def and( xs: List[Boolean] ): Boolean = {
+    reduce(xs)(Monoid(true)( _ && _ ))
+  }
+
+  //Retourne le min et le max d'une liste de doubles en un seul parcours
+  def minMax( as: List[Double] ): (Double,Double) = {
+    mapReduce(as)(a => (a,a), tuple2M(Monoid(Double.MaxValue)(_ min _), Monoid(Double.MinValue)(_ max _)))
+  }
+
+  //Série 3: Even
+  def even[A]( as: List[A] ): Boolean = {
+    mapReduce(as)(a => true, Monoid(true)((a,b) => !a))
+  }
+
+  //Compte le nombre d'occurence de chaque objet de la liste
+  def count[A]( as: List[A] ): Map[A,Int] = {
+    mapReduce(as)((a: A) => Map(a -> 1), mapM[A,Int](Monoid(0)(_ + _)))
+  }
+
+  //Compte le nombre d'occurence de chaque pair d'objet
+  def count2[A,B]( as: List[(A,B)] ): Map[A,Map[B,Int]] = {
+    mapReduce(as)((a: (A,B)) => Map[A,Map[B,Int]](a._1 -> Map[B,Int](a._2 -> 1)), mapM[A,Map[B,Int]](mapM[B,Int](Monoid(0)(_ + _))))
+  }
+
+  //Série 4: forall
+  def forall[A]( ps: List[(A)=>Boolean] ): (A)=>Boolean = {
+    reduce(ps)(Monoid((a:A) => true)((f,g) => (a:A) => f(a) && g(a)))
+  }
+}
diff --git a/base_tp/test/8.monoidTest.scala b/base_tp/test/8.monoidTest.scala
new file mode 100644
index 0000000..736232e
--- /dev/null
+++ b/base_tp/test/8.monoidTest.scala
@@ -0,0 +1,63 @@
+package ch.hepia.tpscala.monoid
+
+import org.scalatest.funsuite.AnyFunSuite
+
+import Examples._
+
+class Monoid8Suite extends AnyFunSuite {
+
+
+  test( "minMax" ) {
+    assert( minMax( List(1,2,3,4,5,6) ) == (1,6)  )
+    assert( minMax( List(1) ) == (1,1)  )
+    assert( minMax( List(2,2,2,2,2,2,2,2) ) == (2,2) )
+    assert( minMax( List(100,-100) ) == (-100, 100) )
+  }
+
+  test( "and" ) {
+    assert( and(Nil) && and(List(true)) == true )
+    assert( and(List( true, true, true ))  == true )
+    assert( and(List( true, true, false ))  == false )
+  }
+
+  test( "even" ) {
+    assert( even( Nil ) == true )
+    assert( even( List(0) ) == false )
+    assert( even( List(0,0) ) == true )
+    assert( even( List(0,0,0) ) == false )
+    assert( even( List(0,0,0,0) ) == true )
+    assert( even( List(0,0,0,0,0) ) == false )
+  }
+
+
+  test( "count" ) {
+    assert( count( List("y", "n", "y", "y", "n", "y") ) == Map( "y"->4, "n"->2 ) )
+    assert( count(Nil) == Map() )
+    assert( count( List(1,2,3,4,5) ) == Map( 1->1, 2->1, 3->1, 4->1, 5->1 ) )
+  }
+
+  test( "count2" ) {
+    assert(
+      count2( List( ("a","y"), ("a", "y"), ("b","y"), ("b","n"), ("c","y") ) ) ==
+        Map(
+          "a" -> Map("y"->2),
+          "b" -> Map ("y"->1, "n"->1),
+          "c" -> Map("y"->1 )
+        )
+    )     
+  }
+
+  test( "forall" ) {
+    val big = (_:Int) >= 100
+    val even = (_:Int) % 2 == 0
+    val mul3 = (_:Int ) % 3 == 0
+    val ps = forall( List( big, even, mul3 ) )
+    assert( ps( 402 ) )
+    assert( ! ps( 200 ) )
+    assert( forall( Nil )( 402 ) )
+    assert( forall( Nil )( 200 ) )
+  }
+
+
+
+}
-- 
GitLab