∈"> ∉"> →"> ←"> ]>
//Fichier HelloWorld.scala
object HelloWorld {
def main(args: Array[String]) {
println("Bonjour tout le monde !")
}
}
Compilation et exécution :
$ scalac HelloWord.scala
$ scala HelloWorld
Bonjour tout le monde !
$ scala
Welcome to Scala 2.11.11 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_144).
Type in expressions for evaluation. Or try :help.
scala> object HelloWorld {
| def main (args : Array[String]) {
| println("Bonjour tout le monde !")
| }
| }
defined object HelloWorld
scala> HelloWorld.main(new Array[String](0))
Bonjour tout le monde !
scala> HelloWorld.main(new Array(0))
Bonjour tout le monde !
scala> HelloWorld.main(Array("foo", "bar"))
Bonjour tout le monde !
scala> HelloWorld.main(Array.empty)
Bonjour tout le monde !
Que peut-on dire sur ce code ?
C'est juste assez pour faire le TP (et éveiller votre curiosité)
Deux modes de définitions de variables :
var x : Int = 0
val y : Int = 0
x = x + 1 //affiche x: Int = 1
y = y + 1 //erreur : reassignment to val
//On peut omettre les types
val s = "Bonjour" // affiche s : String = "Bonjour"
val v = 42 // affiche v : Int = 42
//On peut omettre les valeurs, mais il faut donner le type
val u : Int
val z : Double
{
e1
e2
…
en
}
if (e)
b1
else
b2
while (e)
b
for (x <- e if c1; … ;if cn)
b
for (x1 <- e; …; xn <- en )
b
//est équivalent à
for (x1 <- e1)
…
for (xn <- en)
b
def functionName(x1: T1, …, xn:Tn) : S = {
…
}
//appel
functionName(v1, …, vn)
def fibonacci(n : Int) = {
def fibo_aux(i : Int, accu1 : Int, accu2 : Int) : Int = {
if (i > n) {
accu1
} else {
fibo_aux (i+1, accu1+accu2, accu1) }
}
fibo_aux(0, 1, 0)
}
val next = (x : Int) => x + 1
def apply (f : Int => Int, v : Int) = { f (v) }
def ident[A]( x : A) = { x }
apply(next, 3)
apply(ident, "Hello")
apply(ident, 42)
Encore plein d'autres choses (fonctions variadiques, arguments optionnels, application partielle, appel paraisseux, …).
De nombreuses classes de collections en Scala :
val tab = new Array[String](4) //initialisé à null
//dans toutes les cases
tab(0) = "a"
tab(1) = "b"
tab(2) = "c"
println(tab(2))
val tab = Array("b", "c", "d", "e", "f")
tab.length
Pas besoin de mettre le type des éléments s'il peut être inféré
Listes fonctionnelles imuables à la OCaml avec pattern matching !
val l = 1 :: 2 :: 3 :: Nil
val l1 = List(1,2,3) // equivalent
val l2 = 0 :: l1 // l1 n'est pas modifiée
val l3 = l1 :+ 4 // ajout en fin, l1 n'est pas modifiée
val l4 = l2 ::: l3 // concaténation
def length[A](l : List[A]) : Int = {
l match {
case Nil => 0
case _ :: ll => 1 + length(ll)
}
length(l4)
val s1 = Set(1,2,3,4,5) //immuable, package ouvert par défaut
val s2 = scala.collection.mutable.Set(1,2,3,4,5) // mutable
val s3 = s1 + 6 // ajout
val s4 = s1 - 2 // suppression
s4.contains(3) // test
val s5 = s2 - 1 // suppression pas d'effet
s2.remove(1) // effet de bord, s2 contient 2,3,4,5
s2.add(8) // effet de bord s2 contient 2,3,4,5,8
Les méthodes .add/.remove renvoie un Boolean valant true si l'ensemble a été modifié
val m1 = Map("A" -> 1, "B" -> 10, "C" -> 30)
val m2 = scala.collection.mutable.Map("A" -> 1, "B" -> 10, "C" -> 30)
m1("B") // renvoie 10
m1("F") // Exception levée
m1.get("B") // renvoie Some(10)
m1.get("F") // renvoie None
m1 + ("D" -> 50) // renvoie une nouvelle map
m1 - "A" // renvoie une nouvelle map
m2("A") = 10 //mise à jour
remarque : e1 -> e2 est juste une notation pour (e1, e2)
val m = Map("A" -> 10, "B" -> 20)
m.get("A").isDefined // true
m.get("A") // Some(10)
m.get("C").isDefined // false
m.get("C").orElse(50) // renvoie la valeur contenue dans
// le Some ou 50 si l'argument est None
m.get("C") match {
case None => 50
case Some(x) => x
}
En Scala toutes les valeurs sont des objets. Le compilateur et la JVM se chargent automatiquement de convertir en types de bases si besoin
1.toString // "1"
1.to(10) // Range(1,2,3,4,5,6,7,8,9,10)
'A'.range('Z')
val f = (x : Int) => x + 1
val g = f.andThen(f)
g (1) // 3
"1234".toInt // 1234
class Shape(cx : Int, cy : Int) {
val x = cx
val y = cy
override def toString() = {
"(" + x + ", " + y + ")"
}
def this() = {
this(0,0)
}
}
class Circle(cx : Int, cy : Int, cr : Int) extends Shape(cx, cy) {
private var theRadius = Math.max(cr,0)
def radius = theRadius
def radius_= (newRadius : Int) = {
if (newRadius >= 0)
theRadius = newRadius
}
}
val c = new Circle(0,0, 10)
c.radius // appelle c.radius 10
c.radius = 3 // appelle c.radius_=(3)
On a accès aux types de la bibliothèque standard Java :
class Foo (value : Int) extends java.lang.Comparable[Foo] {
val v = value
def compareTo(other : Foo) = {
if (v < other.v) -1
else if (v > other.v) 1
else 0
}
}
object Bar {
val x = 0
val y = 3
def doSomething() { … }
}
Bar.doSomething()
Bar.x
Bar.y
Objet qui sont les seuls habitants de leur classe
Usuellement utilisé pour y stocker des constantes et méhodes « statiques »
C'est dans un objet singleton qu'on met le "main" du programme
Framework de calcul distribué
On s'intéresse ici à l'API « core » qui se place au même niveau que Map/Reduce. Les autres composants (SQL, Streaming, Machine Learning) sont construits au dessus.
Problèmes d'interface avec le programmeur :
Problèmes de performances :
Supposons une transformation qui s'exécute comme une opération M suivie de R1 et une autre qui s'éxécute comme M suivi de R2
Si on veut les 2 résultats, on va calculer 2 fois M
Si on ne veut pas calculer 2 fois M :
Un Resilient Data Set (ensemble de données persistent) est une abstraction de haut niveau qui représente un calcul (et non pas son résultat) sur des données
Les RDDs sont à la base des transformations Spark
Le chargement des données crées un nouveau RDD (les données ne sont pas chargée, on crée juste une structure qui, quand elle sera évaluée chargera les données
On peut composer ds RDDs au moyen de transformations
On peut exécuter une action sur un RDD. Cela déclanche le calcul de toute la transformation pour obtenir un résultat final
Les fonctions de transformation sont les itérateurs Scala, plus quelques nouveaux:
Les actions exécutent le RDD auquels elles sont appliquées pour renvoyer un résultat:
Spark est fourni avec des interpréteurs standards pour Scala, Python (2.7) et R. Ce sont les interpréteurs normaux, dans lesquels sont préchargés les bibliothèques pour Spark.
Pour exécuter un programme Scala, on peut aussi l'exporter comme un jar et utiliser la commande spark-submit pour l'exécuter.