JeuWeb - Crée ton jeu par navigateur
[Scala] Fonction valeur & Currying - Version imprimable

+- JeuWeb - Crée ton jeu par navigateur (https://jeuweb.org)
+-- Forum : Discussions, Aide, Ressources... (https://jeuweb.org/forumdisplay.php?fid=38)
+--- Forum : Programmation, infrastructure (https://jeuweb.org/forumdisplay.php?fid=51)
+--- Sujet : [Scala] Fonction valeur & Currying (/showthread.php?tid=5022)



[Scala] Fonction valeur & Currying - srm - 27-07-2010

Bonjour,

Ce qu'il faut savoir avec Scala c'est qu'il s'agit d'un langage à la fois procédurale et fonctionnel.

Ici je vais donc présenter son aspect fonctionnel avec les fonctions valeurs.

L'exemple que je vais traiter peut-être encore plus performant et sexy mais nécessite d'aborder d'autres notions de Scala et d'être à l'aise avec les notions syntaxiques Scala et donc je n'en parlerais pas ici.

Prennons donc un cas simple, vous voulez faire une fonction qui calcule la somme de tous les éléments d'un tableau.

En Scala ça donnera donc à peu près ceci :
Code PHP :
<?php 
object Main
extends Application {

def sumArray(ar : Array[Int]) : Int = {
var
sum : Int = 0
for (i <- 0 until ar.length) {
sum += ar(i)
}
sum
}

val myArray = Array(3, 8, 12, 7, 4)

println(sumArray(myArray))

}

Résultat : 34

Code plutôt trivial et semblable aux autres langages, par rapport à ce que je disais plus haut, si on faisait la fonction sumArray en utilisant la puissance de Scala ça donnerait plutôt ça :
Code PHP :
<?php 
def sumArray
(ar : Array[Int]) : Int = {
ar.foldLeft(0) { _ + _ }
}

Parenthèse fermée Wink

Donc dans l'exemple donné ce qui peut vous embêter est la notation du for, mais elle est assez transparente :
Code PHP :
<?php 
0 until ar
.length
0 va varier de ... 0 jusqu'à la taille du tableau ar - 1 (car nous avons utilisé le mot clé "until" et non le mot clé "to")
Pour varier de 0 à taille du tableau on aurait donc dit :
Code PHP :
<?php 
0 to ar
.length

Bon jusque là rien de compliqué.
Donc le Scala étant aussi un langage fonctionnel, toutes les fonctions sont des "valeurs", comme le sont les variables par exemple, il n'y a aucune différence pour le langage et donc lorsque l'on utilise une variable on peut aussi utiliser une fonction et vice versa.

Mais à quoi ça sert ? A factoriser le code pour ne citer qu'un cas.

Toujours dans l'exemple plus haut notre fonction calcule la somme de tous les éléments d'un tableau, admettons que nous voulons aussi calculer la somme de tous les éléments pairs d'un tableau, on ferait quelque chose du genre :
Code PHP :
<?php 
object Main
extends Application {

def sumArrayPair(ar : Array[Int]) : Int = {
var
sum : Int = 0
for (i <- 0 until ar.length) {
if (
ar(i) % 2 == 0)
sum += ar(i)
}
sum
}

val myArray = Array(3, 8, 12, 7, 4)

println(sumArrayPair(myArray))

}

Résultat : 24

Cette fonction est très similaire à la précédente, il n'y a que le if qui a été rajouté en gros.

On va donc changer ça pour factoriser le code entre sumArray et sumArrayPair, on va modifier sumArray
pour lui ajouter un second paramètre qui va servir à "filtrer" les éléments du tableau que l'on va ajouter.

Ca donne ceci :
Code PHP :
<?php 
def sumArray
(ar : Array[Int], cond : Int => Boolean) : Int = {
var
sum : Int = 0
for (i <- 0 until ar.length) {
if (
cond(ar(i)))
sum += ar(i)
}
sum
}

On a donc un argument en pus qui porte le nom "cond" qui prend en entrée un entier et qui retourne un booléen.
On l'utilise dans notre if :
Code PHP :
<?php 
if (cond(ar(i)))

Il suffit ensuite d'appeller la fonction de la façon suivante :
Code PHP :
<?php 
// pour ajouter tous les nombres
println(sumArray(myArray, i => true))
// pour ajouter les nombres pairs
println(sumArray(myArray, i => i % 2 == 0))

Sans println l'utilisation de la fonction revient donc à :
Code PHP :
<?php 
sumArray
(myArray, i => i % 2 == 0)
Bon c'est un peu bizarre, surtout si on a plein de lignes de codes pour définir la condition
que l'on veut.

Ca reste cependant sexy car on a factorisé notre fonction et on a plus de code en double Smile

Le currying intervient pour rendre les choses plus sexy, le currying permet de décomposer les arguments.
Ainsi au lieu d'avoir :
Code PHP :
<?php 
def sumArray
(ar : Array[Int], cond : Int => Boolean)
On peut avoir :
Code PHP :
<?php 
def sumArray
(ar : Array[Int])(cond : Int => Boolean)

Ce qui permet donc maintenant d'appeler la fonction ainsi :
Code PHP :
<?php 
sumArray
(myArray)(i => i % 2 == 0)

C'est déjà un peu mieux, on a d'un coté les vrais arguments de contenu et de l'autre ceux pour le contrôle.
Mais quand on a currying une fonction, on peut donc soit l'appeller ainsi :
Code PHP :
<?php 
sumArray
(myArray)(i => i % 2 == 0)
Mais aussi :
Code PHP :
<?php 
sumArray
(myArray) { i => i % 2 == 0 }

Et c'est là que ça devient tout de suite plus sexy, car les { } sont les délimiteurs utilisés en Scala pour séparer
des blocs d'instructions (dans les if, dans les fonctions, dans les méthodes)

Ceci dit ça n'est qu'un exemple bateau avec qu'une partie de la puissance de Scala, en vrai on ferai
plutôt ça ainsi :
Code PHP :
<?php 
def sumArray
(ar : Array[Int])(condition : (Int, Int) => Int) = {
ar.foldLeft(0) { condition }
}
Ou encore :
Code PHP :
<?php 
def sumArray
(ar : Array[Int])(condition : (Int, Int) => Int) = {
(
0 /: ar) { condition }
}

Qui peut donc être utilisé ainsi :
Code PHP :
<?php 
sumArray
(ar) { (i, j) => i + J }

sumArray(ar) { (i, j) =>
if (
j % 2 == 0) i + j
else i
}

Bien entendu avec cette dernière forme beaucoup plus "Scala" et fonctionnel, ça donne beaucoup plus de possibilités Smile


RE: [Scala] Fonction valeur & Currying - niahoo - 27-07-2010

Sympa, le truc qui me manque en php ce sont les typages fixes. ( et fort aussi mais là autant changer de langage )




Voici comment je traduirais ton truc en php ( que 5.3 , en 5.2 ça marche pas et je regrette fort ! )

Code PHP :
<?php

function sumArray( $ar , $cond ) {

$sum = 0;
foreach(
$ar as $val )
if(
$cond( $val ) )
$sum += (int) $val;
return
$sum;
}

$t = array( 1,2,3,4,5,6,7,8,9,10 );
// bon c'est moins sexy c'est sûr :
echo 'En utilisant une lambda: ' , '<br/>', PHP_EOL;
echo
'Somme: ', sumArray( $t, function ($v){ return !($v % 2);} ) , '<br/>', PHP_EOL; // on envoie un fonction lambda

// Sinon avec une closure :

$divisible = function ( $dividende ) {
return function (
$v ) use ( $dividende ) { // on renvoie une closure
return !($v % $dividende);
};
};

echo
'En utilisant les closures: ' , '<br/>', PHP_EOL;
$t = array( 1,2,3,4,5,6,7,8,9,10 );
echo
'Somme: ', sumArray( $t, $divisible(2) ) , '<br/>', PHP_EOL;
echo
'Somme: ', sumArray( $t, $divisible(3) ) , '<br/>', PHP_EOL;
echo
'Somme: ', sumArray( $t, $divisible(4) ) , '<br/>', PHP_EOL;
echo
'Somme: ', sumArray( $t, $divisible(5) ) , '<br/>', PHP_EOL;
echo
'Somme: ', sumArray( $t, $divisible(6) ) , '<br/>', PHP_EOL;
echo
'Somme: ', sumArray( $t, $divisible(7) ) , '<br/>', PHP_EOL;

par contre les
Code :
var sum : Int = 0
c'est vraiment pas sexy,
ils auraient pas pu faire un bon vieux
Code :
Int sum = 0


________________________________

tu peux détailler ça :

Code :
def sumArray(ar : Array[Int])(condition : (Int, Int) => Int) = {
  (0 /: ar) { condition }
}

et ça ?

Code :
def sumArray(ar : Array[Int]) : Int = {
    ar.foldLeft(0) { _ + _ }
  }



RE: [Scala] Fonction valeur & Currying - srm - 27-07-2010

Justement non.
Le mot clé var est très important, il est d'ailleurs à éviter autant que possible et il faut privilégié val.
Donc :
val i = 0
var j = 0
Si tu mets que ça, il infère le type en entier, tu peux aussi mettre :
val i : Int = 0
var j : Int = 0

Quelle est la différence ?
Et bien quand tu mets val tu ne peux plus changer le contenu de ta variable (val = valeur), tandis que lorsque tu mets var tu peux changer le contenu de ta variable (var = variable)

Si donc par exemple tu fais :
val monObjet = new MaClasse()
Tu es assuré que monObjet pointe toujours sur la même référence d'objet vu que tu as mis "val" et que ça ne peut plus être changé Smile

Pour le détail des deux derniers exemples je le fais plus tard dans la soirée, je ne vais pas tarder à manger et ça prend du temps à expliquer Smile


RE: [Scala] Fonction valeur & Currying - niahoo - 27-07-2010

ok, mais sinon dans le cadre d'un jeu tu t'en sers comment de ce langage ? On peut faire un serveur avec, ouvrir des connexions depuis un programme php ou autre CGI ?


RE: [Scala] Fonction valeur & Currying - srm - 27-07-2010

Tu peux soit t'en servir pour la totalité avec le framework Lift, mais ça reste du Java et pour le moment je n'aime pas trop côté web.

Pour moi c'est plutôt côté back-office qu'il est intéressant, ce langage Scala à son nom à cause du fait qu'il est très orienté Scalabilité, il est donc très performant sur les threads les et tout ce qui est orienté extension horizontale (rajouter des serveurs plutôt qu'augementer la puissance des serveurs), par exemple mon topic Actor de Scala met en avant le côté multi thread.

Je vais en faire un qui parle des sockets quand j'aurais eu le temps de faire un truc un peu clair à ce sujet Smile

Donc pour tout ce qui est moteur du jeu, traitement etc qui est côté serveur : Scala rox à mort Smile


RE: [Scala] Fonction valeur & Currying - srm - 10-08-2010

Alors j'ai un peu de temps pour expliquer ça :
Code PHP :
<?php 
def sumArray
(ar : Array[Int])(condition : (Int, Int) => Int) = {
(
0 /: ar) { condition }
}

def sumArray(ar : Array[Int]) : Int = {
ar.foldLeft(0) { _ + _ }
}

Donc normalement je n'ai à expliquer que ces deux lignes :
Code PHP :
<?php 
(0 /: ar) { condition }
Code PHP :
<?php 
ar
.foldLeft(0) { _ + _ }

On utilise ici la notion "Sugar" qui est une façon alléger d'écrire du code Scala, des raccourcis qu'on a la liberté de prendre ou non.

/: est tout simplement une autre façon d'écrire foldLeft et :\ foldRight
Et concernant _, c'est en effet un caractère magique pour plusieurs choses, ici il se substitue à la variable logiquement à utiliser car il n'y a pas 50 choix par défaut.

La façon plus longue d'écrire :
Code PHP :
<?php 
ar
.foldLeft(0) { _ + _ }
est :
Code PHP :
<?php 
ar
.foldLeft(0) { (total, value) => total + value }



RE: [Scala] Fonction valeur & Currying - niahoo - 10-08-2010

ok merci.

bon c'est pas très lisible quand on connait pas .. Smile