Fonction
partielle
Une fonction partielle
est une fonction avec un domaine de définition restreint. Elle n'est pas entièrement
définie sur l'ensemble des valeurs du type correspondant à l'ensemble de
définition. Par exemple, une fonction qui prend un Integer en paramètre et qui
n’est pas définie pour toutes les valeurs possibles Integer : la fonction racine
carrée n’est pas définie pour les nombre négatifs, la fonction division (x, y)
qui à x associe x/y n'est pas définie pour y=0.
En scala une fonction
partielle est du type trait PartialFunction[-A,
+B] qui étend la fonction unaire ((A)
=> B) :
trait PartialFunction[-A, +B] extends (A) ⇒ B
Une fonction partielle
Scala doit obligatoirement définir 2 méthodes : apply et isDefinedAt. Voici quelques exemples de fonction partielle en Scala :
- List [+A]:
def isDefinedAt(x: Int): Boolean
//Tests whether this sequence contains given index.
def apply(n: Int): A
//Selects an element by its index in the sequence.
- Map [A, +B] :
def isDefinedAt(key: A): Boolean
//Tests whether this map contains a binding for a key.
def apply(key: A): B
//Retrieves the value which is associated with the given key.
Exemple
pratique
Nous disposons d’une
liste de nombres réels. L’objectif ici est de calculer la racine carrée de chaque
nombre positif de la liste. Il faudra en effet omettre les nombres négatifs.
Solution avec Java (<8) :
Solution avec Java (<8) :
public class Main {
public static void main(String[] args) {
List<Double> result = square(Arrays.asList(-4.0, 1.0, 16.0, 36.0, -81.0, 49.0));
System.out.println("Reslutat : "+result);
}
static List<Double> square(List<Double> inputs){
List<Double> result = new ArrayList<Double>();
for (Double value : inputs){
if(value >=0){
result.add(Math.sqrt(value));
}
}
return result;
}
}
Cette solution procédurale effectue un parcours afin d'omettre les nombre négatifs de la liste. Voyons comment implémenter une solution fonctionnelle avec Scala en utilisant la fonction collect.
Scala dispose d’une fonction collect qui s’applique à une liste. Elle prend en paramètre une fonction partielle (fp) et l’applique à chaque élément de la liste. Pour chaque élément e de la liste, la fonction collect appelle fp isDefinedAt(e). Si cette application retourne true, fp apply(e) est appelée sinon l'élément est omis. L’exemple ci-dessus applique la fonction partielle ages du type Map sur une liste de noms names :
Scala dispose d’une fonction collect qui s’applique à une liste. Elle prend en paramètre une fonction partielle (fp) et l’applique à chaque élément de la liste. Pour chaque élément e de la liste, la fonction collect appelle fp isDefinedAt(e). Si cette application retourne true, fp apply(e) est appelée sinon l'élément est omis. L’exemple ci-dessus applique la fonction partielle ages du type Map sur une liste de noms names :
Une fonction partielle peut être utilisée conjointement avec la fonction collect afin d’implémenter élégamment le problème de calcul de la
racine carrée d’une liste de nombre réels :
def main(args: Array[String]) = {
val result = List(-4.0, 1.0, 16.0, 36.0, -81.0, 49.0) collect square
println("result, " + result)
}
val square = new PartialFunction[Double,Double]{
def apply(v1: Double): Double = Math.sqrt(v1)
def isDefinedAt(x: Double): Boolean = x match {
case x => x > 0
case _ => false
}
}
Une solution équivalente simplifiée en utilisant case :
def main(args: Array[String]) = {
val result = List(-4.0, 1.0, 16.0, 36.0, -81.0, 49.0) collect square
println("result, " + result)
}
def square : PartialFunction[Double, Double] = {
case value if(value >= 0) => Math.sqrt(value)
}
Combinaison
de fonctions partielles
Commençons par un petit
exercice. Nous disposons d’une liste d’entiers et nous voulons multiplier les
nombres impairs par 3 et les nombres pairs par 2. Nous allons donc définir deux
fonctions partielles. Une fonction partielle pour les nombres impairs et une
autre pour les nombres pairs. Il suffit de combiner les deux fonctions
partielles avec la fonction orElse. Cette fonction s’applique de la manière
suivante :
fp1(e) orElse fp2(e).
Si la fonction partielle fp1 n’est pas applicable au paramètre e (isDefinedAt(e)=false), fp2 est appliquée au paramètre e. notre implémentation va consister à appliquer la fonction collect avec comme paramètre la combinaison de deux fonctions partielles :
def main(args: Array[String]) = {
println("result : " + (List(3, 7, 4, 5, 8, 10, 6, 13).collect(multiplyOddNumber orElse multiplyPeerNumber)) )
}
def multiplyOddNumber: PartialFunction[Int, Int] = {
case x if x % 2 == 1 => 3*x
}
def multiplyPeerNumber: PartialFunction[Int, Int] = {
case x if x % 2 == 0 => 2*x
}
result : List(9, 21, 8, 15, 16, 20, 12, 39)
La combinaison de nos deux fonctions partielles (multiplyOddNumber orElse multiplyPeerNumber ) nous donne une fonction totale c'est-à-dire une nouvelle fonction définie sur l'ensemble des valeurs du type correspondant à l'ensemble de définition. Nous pouvons donc utiliser la fonction map à la place de collect :
def main(args: Array[String]) = {
println("input, " + (List(3, 7, 4, 5, 8, 10, 6, 13).map(multiplyOddNumber orElse multiplyPeerNumber)) )
}
Aucun commentaire:
Enregistrer un commentaire