EnsiRef

Programmation fonctionnelle

Cette page résume le cours de programmation fonctionnelle

#Déclaration

#Liste

scala> val liste = List (99, 42, 34, 65)
liste : List[Int] = List (99, 42, 34, 65)

scala> liste(2)
res0: Int = 34

scala> liste.head
res1: Int = 99

scala> liste.tail
res2: List[Int] = List(42, 34, 65)

scala> 14 :: 45 :: 21 :: Nil
res3: List[Int] = List (14, 45, 21)

#Tuple

scala> val paire = (99, " luftballon ")
paire: (Int , String ) = (99 , luftballon )

scala> paire._1
res0: Int = 99

scala> paire._2
res1: String = luftballon

scala> val triplet = (4, 2, 1)
Triplet : (Int, Int, Int) = (4, 2, 1)

#Fonction

def max(x: Int , y: Int) : Int = {
    if (x > y) x
    else y
}

OU

def max(x: Int , y: Int) = if (x > y) x else y

Il est possible d'ajouter des fonctions en paramètre :

def somme(f: Int => Int, a: Int, b: Int) : Int =
    if (a > b ) 0
    else f(a) + somme(f, a+1, b)

#Classe

class Rationnel (n: Int , d: Int){
 // Fonction privée
 private def pgcd(a: Int , b: Int): Int = if (b == 0) a else pgcd(b, a % b)

 // Variable de classe
 val numer = n/( pgcd(n,d) abs)

 val denom = d/( pgcd(n,d) abs)

 // Override de fonction
 override def toString = s"$numer/$denom"

 // Fonction publique
 def plus(autre: Rationnel ) =
    new Rationnel (numer*autre.denom + autre. numer *denom , denom*autre.denom)

 // Override de calcul
 def +(autre: Rationnel ) =
    new Rationnel (numer*autre.denom + autre.numer*denom , denom*autre.denom)

 def unary_- = new Rationnel (-numer , denom )
 def -(autre : Rationnel ) = this + -autre
}

#Object

object Rationnel {
    // Sert souvent de factory
    def apply(n: Int , d: Int) =if(d<0) new Rationnel (-n,-d) else new Rationnel (n,d)
    implicit def int2Rationnel (n: Int) : Rationnel = new Rationnel (n, 1)
}

Si un objet a le même nom qu'une classe, alors l'objet et la classe sont compagnons. La classe a accès aux valeurs de l'objet.

#Principe avancée

#Fonction curryfiée

def polynome2(a: Double, b: Double, c: Double)(x: Double) : Double =
    a*x*x + b* x + c
scala> val monPoly = polynome2(5, -3.1, 1)_
monPoly: Double => Double = <function>

scala> monPoly(10)
res0: Double = 470.0

scala> val monPoly2 : Double => Double = polynome2(-2,5, 7)
monPoly2: Double => Double = <function>

scala> monPoly2(2)
res1: Double = 9.0

scala>polynome2(5, -2, 3)(3)
res2: Double = 42.0

#Récursivité terminale

La récursion terminale est possible si le dernier appel exécuté dans une fonction est

  • Soit une valeur
  • Soit un appel récursif seul
def est_present(x: Int, l : List[Int]) : Boolean =
    if( l.isEmpty ) false
    else if(l.head == x) true
    else est_present(x, l.tail)

#Pattern matching

Version améliorée d'un switch : Adéquation à un motif.

x match {
 case 1 => "un"
 case Deux => "deux"
 case `trois` => "trois"
 case _ => "inconnu"
}
liste match {
 case Nil => "Liste vide"
 case 1 :: Nil => "Liste contenant 1, et rien d’autre"
 case _ :: Nil => "Liste avec un seul élément"
 case 1 :: 2 :: Nil => "Liste contenant 1, 2 et rien d’autre"
 case x :: xs => s"Liste avec $x en tête, suivant de la liste $xs"
}

#Expressions for

for { i <- 2 until n
 j <- 1 until i
 if estPremier( i+j)
 } yield (i, j)

#Les conteneurs

#Option

Conteneur avec 0 ou 1 élément

  • Permet d'éviter de manipuler des pointeurs null
scala> val optInt = Some(42)
optInt : Option[Int] = Some(42)

scala> val optString = Some("Hello")
optString : Option[String] = Some(Hello)

scala> val lecture = Option(scala.io.StdIn.readLine())
lecture: Option[String] = None 

#Try

  • Permet de gérer les exceptions
scala> val tryInt = Success(42)
tryInt : Try[Int] = Success(42)

scala> val tryString = Success("Hello")
tryString : Try[String] = Success(Hello)

scala> val div = Try( 100/ 0)
div: Try[Int] = Failure(java.lang.ArithmeticException: / by zero)

scala> val resultat : Try[String] = Failure(new Exception("erreur"))
resultat : Try[String] = Failure(java.lang.exception : erreur)

#Future

  • Permet de gérer les fonctions asynchrones
def divisionLente(x: Int, y: Int) = {Thread.sleep(5000); x/y }

scala> val resultat = Future{ divisionLente(4, 2) } // retourne un résultat immédiat
resultat : scala.concurent.Future[Int] = scala.concurent…

val resultatDéfinitif = Await.result(resultat) // bloque pour 5 sec.

#Les collections

#Fonction des collections

#Length

  • length - taille d'une liste

#Take / Drop

  • take(n) - prend les n premiers élements d'un liste
  • drop(n) - laisse tomber les n premier les éléments d'une liste

#Applatir une liste

  • flatten - Transforme une List[List[T]] en List[T]

#Zip / Unzip

  • zip - Renvoie une liste de paires à partir de deux listes
scala> List(0, 1, 2, 3).zip( List("a", "b", "c", "d") )
res0: List[(Int, String)] = List((0, a), (1, b), (2, c), (3, d) )
  • unzip - Renvoie deux listes à partir d’une liste de paires
scala> List((0, "a"), (1, "b"), (2, "c"), (3, "d")).unzip
res0: (List[Int], List[String]) =(List(0, 1, 2, 3), List(a, b, c, d))

#MkString

mkstring - Transforme une liste en chaîne de caractères

#Map

  • map - Renvoie la liste image d'une liste
scala> List(0, 1, 2, 3, 4, 5).map(x=> x*x)
res0: List[Int] = List(0, 1, 4, 9, 16, 25)
  • flatMap - Map suivi de flatten
scala> List("pommes", "oranges", "poires").flatMap( _.toList )
res1: List[Char] = List(p, o, m, m, e, s, o, r, a, n, g, e, s, p, o, i, r, e, s)

#Filtre

  • filter - Renvoie une sous liste filtrée
scala> List(0, 1, 2, 3, 4, 5).filter( _%2==0)
res0: List[Int] = List(0, 2, 4)

#Prédicat

  • exists - Renvoie un boolean si au moins un élément répond à un préficat
scala> List(0, 1, 2, 3, 4, 5).exists( _%2==0)
res0: Boolean = true
  • forall - Renvoie un boolean si tous les éléments répondent à un prédicat
scala> List(0, 1, 2, 3, 4, 5).forall( _%2==0)
res0: Boolean = false

#Réduire une collection

  • reduce - Permet de résuire une collection à une valeur
scala> List(1, 2, 3, 4).reduce( (x,y) => x+y )
res0: Int = 10
  • reduceLeft - reduce qui va de la gauche vers la droite

  • reduceRight - reduce qui va de la droite vers la gauche

  • foldLeft - Réduit une collection à une valeur de type différent de la liste

scala> def op(p: (Int, Int), x: Int) = {
| if (x < p._1) (x, p._2)
| else if (p._2 < x) (p._1, x)
| else p }
op: (p: (Int, Int), x: Int)(Int, Int)

scala> List(3, 1, 4, 2).foldLeft( (3, 3) )(op)
res0: (Int, Int) = (1, 4)