Skip to content
Snippets Groups Projects
Commit 6a84cda2 authored by joel.vonderwe's avatar joel.vonderwe
Browse files

Monoid exos

parent df59cd64
Branches master
No related tags found
No related merge requests found
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)))
}
}
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 ) )
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment