S-99 : P02, P03, P04, P05

Leave a Comment

Avant d'aller plus loin laissez-moi vous dire que d'intéressantes discussions sont en cours sur la mailing liste du PSUG (Paris Scala User group). Je vous encourage à vous inscrire afin de prendre part aux échanges.
Nos oignons maintenant!

Le problème P02

Il s'agit en fait d'écrire une fonction qui retourne l'avant dernier élément d'une liste.
Voici l'exemple donné sur le site du S-99.

scala> penultimate(List(1, 1, 2, 3, 5, 8))
res0: Int = 5


La solution au P02

Voici une des solutions auxquelles je suis arrivé:

def penultimate[T](xs: List[T]) = {
      xs.reverse match {
        case _ :: x :: _ => Some(x)
        case _ => None
      }
    }


Le test de la solution à P02

import org.specs2.mutable._

class P02Spec extends Specification {
  "P02.penultimate(List())" should {
    
    "return None" in {
      P02.penultimate(List()) must_== None
    }

    "return None" in {
      P02.penultimate(List(1)) must_== None
    }

    "return Some(2.5)" in {
      P02.penultimate(List(3.23, 2.4)) must_== Some(3.23)
    }

    "return Some(2)" in {
      P02.penultimate(List(1, 2, 3)) must_== Some(2)
    }

    "return Some(3)" in {
      P02.penultimate(List(1, 2, 3, 4)) must_== Some(3)
    }
  }
}

Le problème P03

Il s'agit d'écrire une fonction qui renvoie l'élément correspondant à un index donné.
Exemple:

scala> nth(2, List(1, 1, 2, 3, 5, 8))
res0: Int = 2


La solution à P03

Avant d'aller plus loin permettez moi de vous dire que cet exercice n'a rien de nécessaire car l'API des listes permet de récupérer l'élément à un index donné de la façon suivante:

val element = xs(index)
Maintenant l'une des solutions que j'ai pu avoir:
def nth[T](index: Int, xs: List[T]):Option[T] = {
    (index, xs) match {  
      case (_, Nil) => None
      case (0, head :: tail) => Some(head)
      case (_, head :: tail) if(index == -1 || index > (xs.length + 1)) => None
      case (_, head :: tail) => nth(index - 1, tail)
    }
  }
Remarquez l'usage du pattern matching sur le tuple formé de l'index et de la liste.

Le test de la solution à P03

Enfin le test:

import org.specs2.mutable._

class P03Spec extends Specification {
  "P03.nth(index, xs)" should {
    
    "return None" in {
      P03.nth(-1, List(1, 3)) must_== None
    }

    "return Some(1)" in {
      P03.nth2(0, List(1, 3)) must_== Some(1)
    }

    "return Some(4)" in {
      P03.nth(2, List(2, 3, 4)) must_== Some(4)
    }

    "return None" in {
      P03.nth2(10, List(2, 3, 4, 5)) must_== None
    }

    "return None" in {
      P03.nth(0, List()) must_== None
    }    
  }
}

Le problème P04

Il s'agit encore une fois d'implémenter une fonctionnalité déjà présente dans l'API des listes mais c'est pour le fun: trouver le nombre d'éléments dans une liste
Exemple:

scala> length(List(1, 1, 2, 3, 5, 8))
res0: Int = 6

La solution au P04

Voici deux solutions à ce problème: l'une utilise la recursivité et l'autre la méthode foldLeft() de l'API des listes.

def lengthRecursive[T](xs: List[T]): Int = {
    xs match {
      case Nil => 0
      case head :: tail => 1 + lengthRecursive(tail)
    }
  }
  
def length[T](xs: List[T]) = {
  xs.foldLeft(0)((x, _) => x + 1)
}


Le test à la solution au P04

import org.specs2.mutable._

class P04Spec extends Specification {
  "P04.length(xs)" should {
    
    "return 0" in {
      P04.length(List()) must_== 0
    }

    "return 4" in {
      P04.length(List(1, 2, 3, 4)) must_== 4
    }

    "return 5" in {
      P04.length(List(1, 2, 3, 4, 5)) must_== 5
    }

    "return 7" in {
      P04.length(List(1, 2, 3, 4, 5, 6, 7)) must_== 7
    }
  }
}


Le problème P05

Il s'agit d'écrire une liste qui inverse une liste (pour info cette méthode existe déjà dans l'API des listes).
Exemple:

scala> reverse(List(1, 1, 2, 3, 5, 8))
res0: List[Int] = List(8, 5, 3, 2, 1, 1)


La solution au P05

def reverse[T](xs: List[T]):List[T] = xs match {
    case Nil => Nil
    case head :: tail => reverse(tail) :+ head
  }
  


Le problème P05

Enfin un petit test pour s'assurer que tout se passe bien!

import org.specs2.mutable._

class P05Spec extends Specification {
  "P05.reverse(xs)" should {
    
    "return Nil" in {
      P05.reverse(List()) must_== Nil
    }

    "return List(4, 3, 2, 1)" in {
      P05.reverse(List(1, 2, 3, 4)) must_== List(4, 3, 2, 1)
    }

    "return List(5, 4, 3, 2, 1)" in {
      P05.reverse(List(1, 2, 3, 4, 5)) must_== List(5, 4, 3, 2, 1)
    }

    "return List(7, 6, 5, 4, 3, 2, 1)" in {
      P05.reverse(List(1, 2, 3, 4, 5, 6, 7)) must_== List(7, 6, 5, 4, 3, 2, 1)
    }
  }
}


Conclusion

J'espère que les quelques solutions vous ont donné envie de suivre les prochains épisodes du S-99. Ne décrochez pas là les problèmes deviennent de plus en plus intéressants!
A bientôt!

S-99 : P01

Leave a Comment

J'ai entrepris de résoudre les problèmes du "S-99: Ninety-Nine Scala Problems", histoire de faire du Scala et peut-être de confronter mes solutions avec celles de mes lecteurs. Donc n'hésitez pas à poster vos solutions en commentaires! J'espère qu'à la fin de cette série de problèmes mon cerveau pensera mieux Scala ;-)


Le problème

Il s'agit d'écrire une fonction qui retourne le dernier élément d'une liste d'entiers.
Exemple fourni par le site:

scala> last(List(1, 1, 2, 3, 5, 8))
res0: Int = 8


Les solutions

Voici ma première implémentation:

def last1[T](xs: List[T]) = xs match {
    case List() => None
    case _ => Some(xs(xs.size - 1))
  }
Vous aurez remarqué que je ne renvoie pas directement le résultat mais plutôt un objet Option.
Voici une deuxième implémentation, plus courte, qui utilise la méthode headOption qui renvoit une Option avec sur l'élément de tête d'une liste. Remarquez que j'inverse d'abord la liste.
def last2[T](xs: List[T]): Option[T] = xs.reverse headOption
Enfin la version récursive:
def recursiveLast[T](xs: List[T]): Option[T] = xs match {
    case Nil => None
    case head :: Nil => Some(head)
    case head :: tail => recursiveLast(tail)
  }


Les tests

Voici le test de mes fonctions avec Specs2:

import org.specs2.mutable._

class P01Spec extends Specification {
  "P01.last1()" should {
    "return Some(8)" in {
      P01.last1(List(1, 1, 2, 3, 5, 8)) must_== Some(8)
    }

    "return Some(3)" in {
      P01.last1(List(1, 2, 3)) must_== Some(3)
    }

    "return Some(2.5)" in {
      P01.last1(List(3.23, 2.4, 5.0, 2.5)) must_== Some(2.5)
    }

    "return None" in {
      P01.last1(List()) must_== None
    }

    "return None" in {
      P01.last1(Nil) must_== None
    }
  }

  "P01.last2()" should {
    "return Some(8)" in {
      P01.last2(List(1, 1, 2, 3, 5, 8)) must_== Some(8)
    }

    "return Some(3)" in {
      P01.last2(List(1, 2, 3)) must_== Some(3)
    }

    "return Some(2.5)" in {
      P01.last2(List(3.23, 2.4, 5.0, 2.5)) must_== Some(2.5)
    }

    "return None" in {
      P01.last2(List()) must_== None
    }

    "return None" in {
      P01.last2(Nil) must_== None
    }
  }

  "P01.recursiveLast()" should {
    "return Some(8)" in {
      P01.recursiveLast(List(1, 1, 2, 3, 5, 8)) must_== Some(8)
    }

    "return Some(3)" in {
      P01.recursiveLast(List(1, 2, 3)) must_== Some(3)
    }

    "return Some(2.5)" in {
      P01.recursiveLast(List(3.23, 2.4, 5.0, 2.5)) must_== Some(2.5)
    }

    "return None" in {
      P01.recursiveLast(List()) must_== None
    }

    "return None" in {
      P01.recursiveLast(Nil) must_== None
    }
  }
}
The END!!! Mais avant de partir sachez que l'API de List offre une méthode (last) pour récupérer le dernier élément d'une liste et une autre (lastOption) pour récupérer un objet Option du dernier élément.
Bye!

En Scala toute classe peut avoir son compagnon!

Leave a Comment

Au commencement était le mot clé static...

Il arrive lors de la définition d’une classe d’avoir besoin de membres (champs ou méthodes) qui ne soient pas liés à une instance particulière de cette classe. En Java le mot clé static est utilisé pour qualifier ce genre de membres. Cela donne la chose suivante dans le code :

package com.nouhoum.tutorials;
 
import java.util.regex.Pattern;
 
public class PhoneNumber {
   private static Pattern PHONE_REGEX = Pattern.compile("0[1-9]([ .-]?[0-9]{2}){4}");
    
   private final String value;
    
   public PhoneNumber(String value) {
       this.value = value;
   }
    
   public String getValue() {
       return value;
   }
    
   public String toString() {
    return value;
   }
    
   public static boolean validate(PhoneNumber phoneNumber) {
       if(PHONE_REGEX.matcher(phoneNumber.value).matches()) 
return true;
else
         return false;
   }
}

Voici un exemple d’utilisation de la classe. Notez la syntaxe utilisée pour accéder aux membres statiques : le nom de la classe (ici PhoneNumber) un point (.) puis le membre statique. Nous verrons la syntaxe est similaire en Scala.
package com.nouhoum.tutorials;
 
import java.util.Arrays;
import java.util.List;
 
public class Main {
   public static void main(String[] args) {
      List<PhoneNumber> numbers = Arrays.asList(
            new PhoneNumber("12345"), new PhoneNumber("0102030405")
         );
          
      for(PhoneNumber number : numbers) {
         if(PhoneNumber.validate(number))
            System.out.println(number + " is valid");
         else
            System.out.println(number + " is not valid");
      }
   }
}

Les membres statiques n’étant liés à aucune instance particulière de la classe on les appelle membres de classe, ils sont plutôt liés à la classe elle-même.


Ouvrons une parenthèse (importante ;-)

Avoir des membres qui ne sont liés à aucune instance d’une classe n’est pas très orienté objet au goût de Scala (et du mien d’ailleurs ;-). Pour remédier à cette entorse aux règles de l’orienté objet Scala introduit un mot-clé object. Ce mot-clé permet de créer un singleton c’est-à-dire un objet dont une seule instance peut exister. Et c’est dans un objet singleton que vont tous les membres statiques (au sens Java) d’un type (class ou trait). Ainsi les membres autrefois statiques sont maintenant membres d’un objet singleton. Ouf l’O.O. est sauvé!!!

object Hello {
  def sayHello(name: String) = println("Hello " + name)
}
 
Hello.sayHello("Toto")

Remarquez que ce n’est pas vous qui créez l’instance de l’objet singleton, le runtime de Scala s’en occupe.


C’est quoi donc un objet compagnon?

Un objet compagnon est d’abord un object (c’est-à-dire un objet singleton). En outre il est associé à un autre type qui soit une classe soit un trait. Non cela ne veut pas dire que la classe va devoir se marier! Il s’agit en fait d’un objet qui a les caractéristiques suivantes:

  • il a le même nom que le type
  • il est défini dans le même fichier source que le type
  • il a accès aux membres privés du type qu’il accompagne

Maintenant voici l’implémentation du premier exemple en utilisant l’objet compagnon:
//File: PhoneNumber.scala
class PhoneNumber(val value: String) {
  override def toString = value
}
 
object PhoneNumber {
  val PhoneRegex = "0[1-9]([ .-]?[0-9]{2}){4}".r
 
  def validate(phone: PhoneNumber) = PhoneRegex.findFirstIn(phone.value) match {
 case Some(s) => true
 case None => false
  }
}

La méthode statique validate() ainsi que la chaîne statique représentant l’expression régulière ont été déplacées dans l’objet compagnon. Voici rapidement un exemple d’utilisation:
//File: Main.scala
val numbers = List(new PhoneNumber("123456789"), new PhoneNumber("0102030405"))
 
numbers.foreach { p: PhoneNumber =>
  if(PhoneNumber.validate(p))
 println(p + " is a valid french phone number")
  else {
    println(p + " is not a valid french phone number")
  }
}


Quelques usages de l’objet compagnon

Création d’objets
Les objets compagnons ont un autre usage assez intéressant : c’est la définition de “factory method” se débarrassant ainsi de l’écriture de new à chaque création d’objet.

val totoNumber = PhoneNumber("123456789")
Cette ligne est sémantiquement équivalente à la suivante :
val totoNumber = PhoneNumber.apply("123456789")

La méthode apply() est implémentée comme suit :
object PhoneNumber {
  def apply(value: String) = new PhoneNumber(value)
}

L'usage d'apply() dans les collections rend leur usage très agréable.

Les extracteurs
Pour faire vite les extracteurs vous donnent un moyen de faire du pattern sur des objets quelconques (c’est-à-dire qui ne sont pas des instances de case classes). C'est ici pour en savoir plus.


Conclusion

Les case classes tirent pleinement partie des objets compagnons car même si vous ne le voyez pas lorsque vous définissons une case classe le compilateur lui crée un objet compagnon avec une méthode unapply() (un extracteur très utile pour le pattern matching), une méthode factory apply() entre autres.

Pour faire simple, si vous êtes un java-iste, mettez vos méthodes et champs statiques dans l’objet compagnon.

Les extracteurs en Scala

Leave a Comment

Les extracteurs sont une fonctionnalité de Scala permettant de faire du pattern matching sur un objet et d’en extraire des propriétés. On a cette fonctionnalité gratuitement avec les case classes mais pas pour les autres types de classes.

Voici rapidement comment cela se passe avec les case classes. Supposons que nous avons une classe Person définie comme suit:

case class Person(firstName: String, lastName: String)

Nous avons une liste de personnes et souhaitons afficher seulement celles dont le nom est “Cohen” (une simple opération de filtrage quoi ;-)). Voici une solution avec le pattern matching et le fait que Person est une case classe.

val persons = List(Person("Elie", "Cohen"), Person("John", "Dupont"), 
                   Person("Steve", "Woz"), Person("Daniel", "Cohen"))
       
persons foreach { person: Person =>
  person match {
    case Person(_, "Cohen") => println(person)
    case _ => ()
 }
}

Cool tout ça! Mais si notre classe n’est pas une case classe que faisons-nous alors? Les extracteurs viennent à notre secours. Pour illustrer mes propos je vais prendre un exemple volontairement simpliste: une application qui affiche des e-mails.

trait Message

class EmailMessage(
  val from: String,
  val to: String,
  val subject: String,
  val content: String
 ) extends Message

Le composant chargé de l’affichage est défini comme suit:

trait MessageDisplayer {
  def display(messages: Seq[Message])
}

Voici une implémentation du trait MessageDisplayer:

class PrintStreamMessageDisplayer(out: PrintStream) extends MessageDisplayer {
  def display(messages: Seq[Message]) {
    messages foreach { message: Message =>
      message match {
        case EmailMessage(from, to, subject, content) =>  
          out.println("============= EMAIL =============")
          out.println("Subject: " + subject)
          out.println("From : " + from)  
          out.println(content)  
        case _ @ unknownMessageType =>
          out.println("Unkown message type" + unknownMessageType)
      }
    }
  }
}

Cette implémentation utilise un objet PrintStream pour afficher les messages. Notez l’usage du pattern matching sur EmailMessage. Ceci est possible grâce à la définition de la méthode unapply() dans l’objet compagnon (campanion object) de la classe EmailMessage.
Cette méthode est définie comme suit:

def unapply(email: EmailMessage) =
  Some(email.from, email.to, email.subject, email.content)

Elle prend en paramètre un objet de type EmailMessage et retourne objet Some contenant les propriété du message EmailMessage. Cette méthode est invoquée lorsque nous faisons :

message match {
        case EmailMessage(from, to, subject, content) =>  …
        ….
} 

Il existe une autre méthode apply() que l’on peut définir dans l’objet compagnon. Celle-ci permet de créer un objet EmailMessage avec la syntaxe :

val email = EmailMessage("tota@toto.com", 
                         "tata@toto.com", 
                         "Hello!", 
                         "Hi! How do you do?")

Remarquez l’absence de new dans le snippet précédent, il invoque la méthode apply() de l’objet compagnon avec les arguments fournis et celle-ci s’occupe des details de création de l’objet.
Le bout de code précédent est équivalent à

val email = EmailMessage.apply("tota@toto.com",
                         "tata@toto.com",
                         "Hello!",
                         "Hi! How do you do?") 

Code final d’EmailMessage:

class EmailMessage(
 val from: String,
 val to: String,
 val subject: String,
 val content: String
  ) extends Message

object EmailMessage {
  def apply(from: String, to: String, subject: String, content: String) =
 new EmailMessage(from, to, subject, content)
  def unapply(email: EmailMessage) =
 Some(email.from, email.to, email.subject, email.content)
}

PrintStreamMessageDisplayer et son campanion object:

class PrintStreamMessageDisplayer(out: PrintStream) 
     extends MessageDisplayer {
  def display(messages: Seq[Message]) {
    messages foreach { message: Message =>
      message match {
        case EmailMessage(from, to, subject, content) =>  
          out.println("============= EMAIL =============")
          out.println("Subject: " + subject)
          out.println("From : " + from)  
          out.println(content)  
        case _ @ unknownMessageType =>
          out.println("Unkown message type" + unknownMessageType)
      }
    }
  }
}

object PrintStreamMessageDisplayer {
  def apply(out: PrintStream) = new PrintStreamMessageDisplayer(out)
}

Enfin le code pour tester tout ça:
object ExtractorsTutorial extends App {
  val emails = Seq(
    EmailMessage("tota@toto.com", "tata@toto.com", "Hello!", "Hi! How do you do?"),
    EmailMessage("john.doe@noname.com", "jane@who.com", "Hello!", "Dear Jane! Who am I ?"),
    EmailMessage("madmax@domain.com", "borat@example.com", "Go", "Get out the way."))

    val displayer = PrintStreamMessageDisplayer(Console.out)
    displayer.display(emails)
}

Son exécution donne:
============= EMAIL =============
Subject: Hello!
From : tota@toto.com
Hi! How do you do?
============= EMAIL =============
Subject: Hello!
From : john.doe@example.com
Dear Jane! Who am I ?
============= EMAIL =============
Subject: Go
From : madmax@domain.com
Get out the way. 

Fin de voyage pour cette première rencontre avec les extracteurs.

© Nouhoum TRAORE.. Fourni par Blogger.