Vous êtes sur la page 1sur 67

R5.

05 : Programmation
Avancée
Rémi Cozot – remi.cozot@univ-littoral.fr
Version 2023 – 2024
R5.05 : Programmation Avancée
Compétences ciblées
▪ Développer — c’est-à-dire concevoir, coder, tester et intégrer — une solution informatique
pour un client. Proposer des applications informatiques optimisées en fonction de critères
spécifiques : temps d’exécution, précision, consommation de ressources…
Apprentissages critiques
▪ AC31.01 | Choisir et implémenter les architectures adaptées
▪ AC32.03 | Choisir et utiliser des bibliothèques et méthodes dédiées au domaine
Savoirs de référence étudiés
▪ Utilisation de frameworks de développement
Prolongements suggérés
▪ Paradigmes de programmation
▪ fonctionnelle, concurrente, répartie, embarquée…
▪ Intelligence artificielle
R5.05 : Programmation Avancée
Compétences ciblées
▪ Développer — c’est-à-dire concevoir, coder, tester et intégrer — une solution informatique
pour un client. Proposer des applications informatiques optimisées en fonction de critères
spécifiques : temps d’exécution, précision, consommation de ressources…
Apprentissages critiques
▪ AC31.01 | Choisir et implémenter les architectures adaptées
▪ AC32.03 | Choisir et utiliser des bibliothèques et méthodes dédiées au domaine
Savoirs de référence étudiés
▪ Utilisation de frameworks de développement → .NET
Prolongements suggérés
▪ Paradigmes de programmation → fonctionnelle F#
▪ fonctionnelle, concurrente, répartie, embarquée…
▪ Intelligence artificielle
Plan
1. Introduction : paradigme de programmation
2. Introduction : .NET
3. Environnement(s) technique(s)
4. F# : premiers pas …
1. Premier exemple
2. Littéral et valeurs
3. Fonctions
4. Structure de contrôle
5. Type
5. Conclusion
Paradigme de programmation
Introduction
Paradigme de programmation
Modèle, approche, modèle de la programmation
▪ Qu’est qu’un code ? Formuler, spécifier une solution informatique à un problème.
▪ Comment décrire cette solution ?
▪ Coder, écrire la solution : Langage adapté au(x) formalisme(s) choisi
Grandes familles de paradigme de programmation
▪ Impérative, structurée, procédurale : C
▪ Orientée Objet : JAVA, python, C++
▪ Orientée Prototype : Javascript
▪ Déclarative
▪ Déclarative
▪ Descriptive : HTML, CSS
▪ Fonctionnelle
▪ Autres
▪ Événementielle (Qt), concurrente, synchrone…
Programmation impérative
Éléments clés : Var, séquence, structure de contrôle
Motivations
▪ Proche de la machine
▪ Langage facile à créer, compiler/interpréter
Limites
▪ Manque de structuration entre données et procédures
▪ Difficile à tester, maintenir ← cadre formel insuffisant
Coder
▪ Définir les données
▪ Imaginer une séquence, qui modifiant les données, donne la solution
Programmation objet
Éléments clés : objet, message, séquence, structure de contrôle
Motivations
▪ Assemblage de briques logicielles appelées objets
▪ Faciliter l’évolution, l’adaptation
Limites
▪ Difficile à tester ← état de l’objet intervient dans l’évaluation
𝑨. 𝒇(𝒙) est dépendant de l’état de 𝑨
Coder
▪ Définir des objets (entités, services, …) qui assemblés calculent la solution
Limite des paradigmes impératif et objet
Vars et attributs mutables → effets de bord,
difficilement testables, prouvables
Exemple: un objet O propose deux méthodes publiques : f(x) et g(y)
▪ On ne peut pas tester du fait des effets de bords, par exemple O.g(y) peut
dépendre d’un appel antérieur O.f(x)
Réaction aux appels de méthode
▪ Séquence de contrôles → pas de possibilité de contrôler à l’exécution
l’évaluation, par exemple, pas d’évaluation paresseuse
Au-delà de l’impératif et de l’objet
Pas de Var → pas d’effet de bord
De vrais fonctions : f(x) → y, quelque-soit le contexte d’évaluation !
Pas de séquence → sémantique claire
▪ Deux fonctions f et g :
▪ f(g(x)) → y = g(x) ; f(y)
▪ f(x) g(x) ≡ g(x) // f(x)
Pas de boucle
▪ Faire n fois quelque chose, si on n’a plus d’effet de bord, on le fait une fois et
c’est bon !+

+ vision idéale
Programmation fonctionnelle
Éléments clés : fonction
Motivations
▪ Composition de fonction
▪ Testable, optimisable, gérable à l’exécution
▪ Un code HTML ne dit pas comment (dans quel ordre) traiter les balises
Limites
▪ Difficile car nécessite souvent de passer un ordre supérieur
𝑓 𝐴 → 𝑔 et 𝑔 𝐴 donne la solution : 𝑓(𝐴)(𝐴)
Coder
▪ Définir de compositions de fonctions (d’ordre supérieure) dont l’évaluation
donne la solution
Programmation fonctionnelle
Intérêts
▪ Testable, voire même prouvable
▪ Haute performance, pendant très longtemps, le langage le plus efficace était
caml (langage fonctionnel pur) même par rapport à du C
▪ Naturellement parallèle, asynchrone
▪ Le contrôle de l’exécution déterminer lors de l’exécution
ℎ 𝑓 𝑥 , 𝑔 𝑥 l’ordre d’exécution libre : 𝑓 𝑥 « puis » 𝑔 𝑥 ou 𝑔 𝑥 « puis » 𝑓 𝑥
Des langages
▪ Lisp, Scheme, Clojure, Erlang, Elixir, Caml, OCaml, Haskell, F#, …
Programmation fonctionnelle
Pourquoi pas plus d’utilisation ?
▪ Jugé (à tort ?) comme difficile
▪ Rapidement : fonction sur des graphes de fonctions
▪ Logique perpendiculaire à l’impératif
▪ Notion d’état est naturelle dans de nombreux cas
▪ Simulation, jeux vidéo : position, vitesse du personnage
▪ Interface graphique, matérielle : position du slider, etc.
▪ Communauté réduite
Tendance : intégrer des aspects fonctionnels dans les langages objets
▪ C++11, C#, Python, Javascript et Java (depuis Java 8)
▪ lambda, map, filter, reduce, partial
.NET
Framework de développement
Framework de développement
Tout n’est pas framework de développement
Node.js
▪ open-source, cross-platform JavaScript runtime env
Express
▪ web framework for Node.js

.NET
▪ free, open-source, cross-platform framework for building modern apps and
powerful cloud services
▪ Framework de développement !
.NET
Multi-plateforme (OS)
▪ Windows, MacOS, Linux, Docker
▪ iOS, Android
Interopérabilité entre langages
▪ Ada for .Net, C#, C++/CLI, ClojureCLR,
F#, F*, IronPython, IronScheme,
PeachPie (A compiler of PHP to .NET),
PowerBuilder, Silverfrost FTN95
(Fortran 95), Visual Basic (VB.NET),
Visual COBOL, Powershell, Xsharp (X#)
Cœur de .NET
▪ CLI : Commun Language Infrastructure
.NET
On ne verra rien (ou presque)
de l’écosystème .NET
▪ Lien avec Azur, Docker,
Blazure, Orleans, Xamarin

.NET →

▪ Création d’une application


de podcast multisupport sur
le cloud
Environnement(s) technique(s)
F# : plein de solutions, donc complexe …
Environnement(s) Technique(s)
La plus simple : mais limité, idéale pour commencer ?
▪ Jupyter F#
▪ Site de référence
▪ https://docs.servicestack.net/jupyter-notebooks-fsharp#generate-f-jupyter-notebooks
▪ https://github.com/dotnet/interactive/blob/main/docs/NotebookswithJupyter.md
La plus complète : attention à l’espace requis !
▪ Visual Studio (pas code)
▪ Site de référence
▪ https://learn.microsoft.com/fr-fr/dotnet/fsharp/get-started/get-started-visual-studio
La plus complexe mais légère
▪ Visual Studio Code + ionide
▪ Site de référence
▪ https://learn.microsoft.com/fr-fr/dotnet/fsharp/get-started/get-started-vscode
F#
Premiers pas …
F# : fonctionnalités
Syntaxe simplifiée
▪ Attention : plusieurs écritures possibles donc difficile à lire !
Immuable par défaut
Inférence de type et généralisation automatique
Fonctions de première classe
Types de données puissants
▪ Classes, interfaces, etc.
Critères spéciaux
Programmation asynchrone
Premier exemple qui en dit long !

// namespaces
open System

// définition d'une valeur nommée


// liste de chaines
let peintres = ["Monet" ; "Cézanne" ; "David" ; "Renoir" ; "Géricault"]
let peintresSuite : (string list) = ["Delacroix" ; "Bonnard" ; "Manet"; "Corot"]

// première fonction
let printList _list =
List.iter (fun item -> printfn $"> {item}") _list

// script
printfn "*** Premier Code ***"
printList peintres
Premier exemple qui en dit long !
Gestion des espaces de nommage
// namespaces Inutile ici, car non utilisé
open System

// définition d'une valeur nommée


// liste de chaines
let peintres = ["Monet" ; "Cézanne" ; "David" ; "Renoir" ; "Géricault"]
let peintresSuite : (string list) = ["Delacroix" ; "Bonnard" ; "Manet"; "Corot"]

// première fonction
let printList _list =
List.iter (fun item -> printfn $"> {item}") _list

// script
printfn "*** Premier Code ***"
printList peintres
Premier exemple qui en dit long !

// namespaces Valeurs nommées ≠ Vars


open System Données sont immuables / immutables
Typage fort inféré ou explicite
// définition d'une valeur nommée
// liste de chaines
let peintres = ["Monet" ; "Cézanne" ; "David" ; "Renoir" ; "Géricault"]
let peintresSuite : (string list) = ["Delacroix" ; "Bonnard" ; "Manet"; "Corot"]

// première fonction
let printList _list =
List.iter (fun item -> printfn $"> {item}") _list

// script
printfn "*** Premier Code ***"
printList peintres
Premier exemple qui en dit long !

// namespaces Définition d’une fonction


open System Typage fort avec généralisation : list<‘a> -> unit
Pas de boucle mais des fonctions sur des listes : List.iter
// définition d'une valeur nommée
// liste de chaines
let peintres = ["Monet" ; "Cézanne" ; "David" ; "Renoir" ; "Géricault"]
let peintresSuite : (string list) = ["Delacroix" ; "Bonnard" ; "Manet"; "Corot"]

// première fonction
let printList _list =
List.iter (fun item -> printfn $"> {item}") _list

// script
printfn "*** Premier Code ***" Définition d’une lambda : fonction anonyme
printList peintres
printfn $« { }» ≈ f « { }» en python
Premier exemple qui en dit long !
Partie de script « exécuté »
// namespaces
open System deviendra pour les applications :
[<EntryPoint>]
// définition d'une valeur nommée let main argv = …
// liste de chaines
let peintres = ["Monet" ; "Cézanne" ; "David" ; "Renoir" ; "Géricault"]
let peintresSuite : (string list) = ["Delacroix" ; "Bonnard" ; "Manet"; "Corot"]

// première fonction
let printList _list =
*** Premier Code ***
List.iter (fun item -> printfn $"> {item}") _list
> Monet
> Cézanne
// script
> David
printfn "*** Premier Code ***"
> Renoir
printList peintres
> Géricault
Premier exemple qui en dit long !

// namespaces
open System
Typage fort avec généralisation avec type générique ‘T
// définition d'une valeur nommée Appel à ToString implicite
// liste de chaines
let peintres = ["Monet" ; "Cézanne" ; "David" ; "Renoir" ; "Géricault"]
let peintresSuite : (string list) = ["Delacroix" ; "Bonnard" ; "Manet"; "Corot"]

// première fonction
let printList (_list : 'T list) : unit =
List.iter (fun item -> printfn $"> {item}") _list

// script
printfn "*** Premier Code ***"
printList peintres
Premier exemple : ce qu’il faut retenir !
Nommage : let nom = …
▪ Aussi bien pour une valeur, une fonction, etc.
sauf type (classe)
Fonction : let nom_fonction param_0 param_1 = corps_fonction
▪ Pas de return explicite : la dernière ligne donne la valeur de retour
Premier exemple : juste pour le fun !

// définition d'une valeur nommée


// liste de chaines
let peintres = ["Monet" ; "Cézanne" ; "David" ; "Renoir" ; "Géricault"]
let peintresSuite : (string list) = ["Delacroix" ; "Bonnard" ; "Manet"; "Corot"]
*** Premier Code ***
// première fonction > Monet
let printList _list = > Cézanne
List.iter (fun item -> printfn $"> {item}") _list > David
_list > Renoir
renvoie la liste
> Géricault
// script > Monet
printfn "*** Premier Code ***" > Cézanne
peintres |> printList |> printList > David
> Renoir
> Géricault
Création de PIPE ! |>
Premier exemple : encore plus fun !
// namespaces

// définition d'une valeur nommée


// liste de chaines
let peintres = ["Monet" ; "Cézanne" ; "David" ; "Renoir" ; "Géricault"]
let peintresSuite : (string list) = ["Delacroix" ; "Bonnard" ; "Manet"; "Corot"]

// première fonction
let printList _list =
List.iter (fun item -> printfn $"> {item}") _list
_list
// deuxième fonction *** Premier Code ***
let about func data = >5
printf $"> {func data}\n" > Monet
data > Cézanne
// script > David
printfn "*** Premier Code ***" > Renoir
peintres |> about (fun a -> a.Length) |> printList > Géricault
Littéral et Valeurs
Valeur, littéral : types de bases
Littéral : constante, potentiellement nommée, nom remplacé par la
valeur à la compilation
▪ Décoré par : [<Literal>]
▪ Commence par une majuscule
Valeur : donnée immuable, évaluée lors de l’exécution

Remarque :
▪ """ … """ permet de définir une chaîne avec des guillemets

[<Literal>]
let ConstJson = """{"Nbs":[1,2,3,4,5]}"""

let chaineJson = """{"Nbs":[1,2,3,4,5]}"""


Types de base
Numériques :
▪ sbyte, byte, int16, uint16, int (int32), uint (uint32), int64, uint64, bigint
▪ simple (float32), double (float64)
Chaînes et caractères
▪ char, string
Fonctions
Le ventricule droit de l’approche fonctionnelle
Définition, appel, composition, lambda

let power x n = let nom_fonction paramètres = corps de la fonction


x**n

let powerOf2 = power 2.0 Évaluation partielle : uniquement premier paramètre !

let power2 x = power x 2.0 let nom_fonction paramètres = corps de la fonction

// tests
let tests = [1.0; 2.0; 3.0]

do
printfn "*** puissance de 2***"
List.iter (powerOf2 >> (fun x -> printfn $"> {x}")) tests
printfn "*** puissance 2***"
List.iter (power2 >> (fun x -> printfn $"> {x}")) tests
Définition, appel, composition, lambda

let power x n =
x**n

let powerOf2 = power 2.0

let power2 x = power x 2.0 Composition de fonction :


F >> G est une fonction : x -> G(F(x))
// tests
let tests = [1.0; 2.0; 3.0]

do
printfn "*** puissance de 2***"
List.iter (powerOf2 >> (fun x -> printfn $"> {x}")) tests
printfn "*** puissance 2***"
List.iter (power2 >> (fun x -> printfn $"> {x}")) tests
Définition, appel, composition, lambda

let power x n =
x**n

let powerOf2 = power 2.0

let power2 x = power x 2.0 Lambda, fonction anonyme :


fun paramètres -> Exp
// tests
let tests = [1.0; 2.0; 3.0]

do
printfn "*** puissance de 2***"
List.iter (powerOf2 >> (fun x -> printfn $"> {x}")) tests
printfn "*** puissance 2***"
List.iter (power2 >> (fun x -> printfn $"> {x}")) tests
Question de style
let power x n = x**n

let powerOf2 = power 2.0


Les deux formes sont totalement équivalentes !
let power2 x = power x 2.0

let squareOf2 = powerOf2 >> power2


let squareOf2bis x = power2 (powerOf2 x)

// tests
let tests = [1.0 .. 3.0]

do
printfn "*** carré des puissances de 2***"
List.iter (squareOf2 >> (fun x -> printfn $"> {x}")) tests
printfn "*** carré des puissances de 2 bis***"
List.iter (squareOf2bis >> (fun x -> printfn $"> {x}")) tests
Question de style
let MorMme (surname : string) (name: string) (male: bool) : string =
if male
then "M. "+surname+" "+name
else "Mme "+surname+" "+name
Fonction comme opérateur
let firstLast f z x y = f x y z

let (-@--) f z x y = f x y z

let Mister = firstLast MorMme true


let Mistress = (-@--) MorMme false

do
printfn $"""{MorMme "Rémi" "Cozot" true}"""
printfn $"""{ firstLast MorMme true "Rémi" "Cozot" }"""
printfn $"""{ Mister "Rémi" "Cozot" }"""
printfn $"""{ Mistress "Anne" "Pacou" }"""
Opérateur

opérateur
let (@@) x y = x + y

do
let op = 1 @@ 2
Utilisation opérateur

let iop = (@@) 1 2

printfn $"1 @@ 2 -> {op}"


printfn $"(@@) 1 2 -> {iop}"
Fonctions récursives
let rec isIn x list =
match list with let rec nom_fonction paramètres = corps de la fonction
| [] -> false
| head :: tail ->
if head = x then true else isIn x tail

let rec count x list =


match list with
| [] -> 0
| head :: tail ->
if head = x
then 1+count x tail
else count x tail
do
printfn $"isIn 3 [1..5] -> {isIn 3 [1..5]}"
printfn $"count 3 [2;3;3;4] -> {count 3 [2;3;3;4]}"
Structures de controle
Structure de contrôle de base
if...then... elif … else let compare x y =
if x = y
then 0
elif x < y then -1
else 1

Structure « peu » fonctionnelle friendly


▪ for...in
▪ for...to
▪ while … do
let seq1 = [for i in 1..10 -> (i, i*i) ]
▪ Nécessite parfois un mutable do
for (x, square_x) in seq1 do
printfn $"square({x}) {square_x}"
Pattern matching : critère

let filterRemiRemy (str : string) : unit =


match str with
| "Rémi" | "Rémy"| "Remi" | "Remy" -> printfn $"> {str} does match!"
| _ -> printfn $"> {str} does not macth!"

let orderTuple (xy: int * int) : (int * int)=


match xy with match ... with
| (x,y) when ((x = 0) || (y=0)) -> (0,0) | ... [| ...][when ...]-> ...
| (x,y) when x > y -> (y,x)
| _ -> xy
| _ -> ...

do
for str in ["René";"Rémi";"rémi";"Remi"] do filterRemiRemy str
printfn $">> {orderTuple (3,0)}" ; printfn $">> {orderTuple (0,1)}"
printfn $">> {orderTuple (3,2)}" ; printfn $">> {orderTuple (1,4)}"
Pattern matching : critère
Parfois peut porter à confusion
| … when … -> …
|… -> if …
let rec insertInSortedList element list =
match list with
| [] -> [element]
| head :: tail ->
if element <= head
then element :: list
else head :: (insertInSortedList element tail)

let rec insertInSortedList2 element list =


match list with
| [] -> [element]
| head :: _ when element <= head -> element :: list
| head :: tail -> head :: (insertInSortedList2 element tail)
Inférence de type : limites !
// arbre binaire
let isLeaf node =
match node with
| _, null, null -> true
| _ -> false
let data node=
let d, _ , _ = node
d
let left node =
let _, l, _ = node
l
let right node =
let _,_, r = node
r
let assemble func leftNode rightNode=
((func (data leftNode) (data rightNode)), leftNode, rightNode)

// test
let node1 = (1.0, null, null)
let node2 = (2.0,null, null)
let node3 = assemble (+) node1 node2
printfn $"> {node3}"
printfn $"> {isLeaf node1}" ; printfn $"> {isLeaf node2}" ; printfn $"> {isLeaf node3}"
Inférence de type : limites !
// arbre binaire
let isLeaf node =
match node with
| _, null, null -> true
| _ -> false
let data node=
let d, _ , _ = node
d
let left node = Erreur sur : isLeaf node3 !
let _, l, _ = node
l
let right node = En explicitant les types, vous
let _,_, r = node essayez de corriger !
r
let assemble func leftNode rightNode=
((func (data leftNode) (data rightNode)), leftNode, rightNode)

// test
let node1 = (1.0, null, null)
let node2 = (2.0,null, null)
let node3 = assemble (+) node1 node2
printfn $"> {node3}"
printfn $"> {isLeaf node1}" ; printfn $"> {isLeaf node2}" ; printfn $"> {isLeaf node3}"
// arbre binaire
let isLeaf (node : 'T*obj*obj) : bool=
match node with
| (_, null, null) -> true
| _ -> false

let data (node: 'T*obj*obj) : 'T =


let d, _ , _ = node
d

let left (node: 'T*obj*obj) : obj =


let _, l, _ = node
l

let right (node : 'T*obj*obj) : obj =


let _,_, r = node
r

let assemble (func: 'T -> 'T -> 'T) (leftNode: 'T*obj*obj) (rightNode: 'T*obj*obj) : 'T*obj*obj =
((func (data leftNode) (data rightNode)), leftNode, rightNode)

// test
let node1 = (1.0, null, null)
let node2 = (2.0,null, null)

let node3 = assemble (+) node1 node2


printfn $"> {node3}" ; printfn $"> {isLeaf node3}"
Type :création de type
Le ventricule gauche de l’approche fonctionnelle
type : alias de type
// alias de type
type nameNb = string*int
type annuaire = nameNb list

// fonctions
let rec isIn(name: string) (annu: annuaire) : bool =
match annu with
| [] -> false
| people :: tail -> let name_, id_ = people in if name_ = name then true else isIn name tail

let rec getByName (name:string) (annu: annuaire) : nameNb =


match annu with
| [] -> ("", -1) // never happens
| (name_, id_) :: tail -> if name_ = name then (name_, id_) else getByName name tail

let rec setByNameIn (name: string) (id:int) (annu: annuaire) : annuaire =


match annu with
| [] -> (name, id)::annu
| (name_ : string, id_: int)::tail ->
if name_ = name
then (name, id)::tail
else (name_, id_)::(setByNameIn name id tail)

let cPrint (name: string) (annu: annuaire) : unit = if isIn name annu then printfn $">> {getByName name annu}" else ()
type : type option
Outils permettant une valeur/attribut optionnel
▪ ≈ Object | None en python
let rec getByName (name:string) (annu: annuaire) : nameNb =
match annu with
| [] -> ("", -1) // never happens
| (name_, id_) :: tail -> if name_ = name then (name_, id_) else getByName name tail

type option, None et Some()


▪ Une valeur de un_type option peut avoir le valeur de un_type ou None
▪ Attention : None ≠ null
type option, None, Some

// alias de type

type nameNb = (string*int) option string*int ou None

type annuaire = nameNb list

let toString (o : nameNb) : string =


match o with
Pattern matching None ou Some
| None -> "< ~ :: ~ >"
| Some((name, id)) -> $"< {name} :: {string(id)} >"

let testAnnu : annuaire = [Some("toto", 1); Some("roro", 2) ]

printfn $"""> {toString (Some ("toto", 1))}"""


Some(string*int) -> string*int option
type option, None, Some
let rec getByName (name: string) (annu: annuaire) : nameNb =
match annu with
| [] -> None
| (people : nameNb) :: (tail: annuaire) ->
match people with
| None -> None
| Some((_name, id)) -> if _name = name then people else getByName name tail

ou
let rec getByName (name: string) (annu: annuaire) : nameNb =
match annu with
| [] -> None
| None :: tail -> None
| Some((_name, id)) ::tail ->
if _name = name then Some((_name, id)) else getByName name tail
type : union

let getPrice (item : Item)=


match item with
| nom of type
| Book(_, price) -> price
type Item = | Object(_, price) -> price

| Book of titre: string * price: float let getIdent (item : Item): Identifier =
match item with
| Object of id: int * price: float | Book(name, _) -> Name(name)
| Object(id,_) -> Id(id)

type Identifier = | Name of string | Id of int let getTitle(book: Item) : string option =
match book with
| Book(name, _) -> Some(name)
| _ -> None

let b = Book("titre", 50.0) in printfn $"{b}"


type : union
type BTree<'T> =| NoneBTree | Node of 'T*('T BTree)*('T BTree) // empty Btree or Node

let buildBTree (value :'T) : BTree<'T > = Node(value, NoneBTree, NoneBTree)

let assemble (assFun : 'T -> 'T -> 'T)(left: 'T BTree) (right :'T BTree) : 'T BTree =
match left with
| NoneBTree -> right
| Node (leftValue, _, _) ->
match right with
| NoneBTree -> left
| Node(rightValue, _ , _) -> Node((assFun leftValue rightValue),left, right)

do
let (first : BTree<int>, second : BTree<int>) = buildBTree(1), buildBTree(2)
let third = assemble (+) first second
printfn $""">> {third} """
Comparaison : F# vs python
En python, en programmation objet ?
type Exp =
| Nb of int
| Add of Exp * Exp
| Mul of Exp * Exp
| Var of string

let rec Eval (env:Map<string,int>) (exp: Exp) : int =


match exp with
| Nb (n: int) -> n
| Add (x: Exp, y: Exp) -> Eval env x + Eval env y
| Mul (x: Exp, y: Exp) -> Eval env x * Eval env y
| Var (id: string) -> env[id]

let env: Map<string,int> = Map ["a",1;"b",2;"c",3]


let ExpTree1: Exp = Add(Var "a", Mul(Nb 2, Var "b"))
let result: int = Eval env ExpTree1
printfn $"{ExpTree1} // {env}-> {result}"
from __future__ import annotations

class Exp :
def Eval(self: Exp, env: dict[str, int]) -> int : ...

Comparaison : F# vs python class Nb(Exp) :


def __init__(self: Nb, value) : self.Nb : int = value
def Eval(self: Nb, env: dict[str, int]) -> int: return self.Nb

En python ?
class Add(Exp):
def __init__(self: Add, left : Exp, right: Exp):
type Exp = self.left : Exp = left
| Nb of int self.right : Exp = right
| Add of Exp * Exp def Eval(self: Add, env: dict[str, int]) -> int:
| Mul of Exp * Exp return self.left.Eval(env) + self.right.Eval(env)
| Var of string
class Mul(Exp):
let rec Eval (env:Map<string,int>) (exp: Exp) : int = def __init__(self: Mul, left : Exp, right: Exp):
match exp with self.left : Exp = left
| Nb (n: int) -> n self.right : Exp = right
| Add (x: Exp, y: Exp) -> Eval env x + Eval env y def Eval(self: Mul, env: dict[str, int]) -> int:
| Mul (x: Exp, y: Exp) -> Eval env x * Eval env y return self.left.Eval(env) * self.right.Eval(env)
| Var (id: string) -> env[id]
class Var(Exp) :
let env: Map<string,int> = Map ["a",1;"b",2;"c",3] def __init__(self: Var, value) : self.Var : str = value
let ExpTree1: Exp = Add(Var "a", Mul(Nb 2, Var "b")) def Eval(self: Var, env: dict[str, int]) -> int:
let result: int = Eval env ExpTree1 return env[self.Var]
printfn $"{ExpTree1} // {env}-> {result}"
env : dict[str, int] = {"a":1,"b":2,"c":3}
ExpTree1 : Exp = Add(Var("a"), Mul(Nb(2), Var("b")))
result : int = ExpTree1.Eval(env)
print(f"{ExpTree1} // {env}-> {result}")
Comparaison : F# vs python
En F# : union de types En python : classes et héritage
▪ 16 lignes ▪ 25 lignes
▪ Eval est centralisée ▪ Eval est redéfinie dans chaque classe
class Exp :
def Eval(self: Exp, env: dict[str, int]) -> int : ...

class Nb(Exp) :
def Eval(self: Nb, env: dict[str, int]) -> int: return self.Nb
let rec Eval (env:Map<string,int>) (exp: Exp) : int =
match exp with class Add(Exp):
| Nb (n: int) -> n def Eval(self: Add, env: dict[str, int]) -> int:
| Add (x: Exp, y: Exp) -> Eval env x + Eval env y return self.left.Eval(env) + self.right.Eval(env)
| Mul (x: Exp, y: Exp) -> Eval env x * Eval env y
| Var (id: string) -> env[id] class Mul(Exp):
def Eval(self: Mul, env: dict[str, int]) -> int:
return self.left.Eval(env) * self.right.Eval(env)

class Var(Exp) :
def Eval(self: Var, env: dict[str, int]) -> int:
return env[self.Var]
Comparaison : F# vs python
En F# : union de types En python : classes et héritage
▪ Égalité des valeurs ! ▪ Égalité des objets

printfn $"{Nb 2} = {Nb 2}-> {(Nb 2)=(Nb 2)}" print(f"{Nb(2)} == {Nb(2)}-> {Nb(2)== Nb(2)}")

>> Nb 2 = Nb 2 -> True >> Nb 2 == Nb 2 -> False

▪ Nécessite une méthode comparaison


type : ajout de membre
Fonction Eval comme membre du type
▪ Associer les fonctions liées au type
dans le type
▪ Appel par notation pointée type Exp =
| Nb of int
| Add of Exp * Exp
... | Mul of Exp * Exp
| Var of string
ExpTree1.Eval env member self.Eval (env:Map<string,int>):int=
match self with
| Nb (n: int) -> n
| Add (x: Exp, y: Exp) -> x.Eval env + y.Eval env
▪ Ça ressemble à de la Programmation | Mult (x: Exp, y: Exp) -> x.Eval env * y.Eval env
| Var (id: string) -> env[id]
Objet ☺ !
▪ Manque que l’héritage !
Conclusion
Programmation fonctionnelle Programmation orientée objet
https://www.data-transitionnumerique.com/tuto-programmation-fonctionnelle
Vars immutables Vars mutables

Possibilité de programmer en parallèle Programmation parallèle non-prise en compte dans le paradigme

Se base sur la notion de fonction Se base sur la notion d’objet

Utiliser la récursivité pour les itérations Utilise les boucles pour les itérations

Pas de changement d’état Changement d’état existant

Pas d’effet de bord Effet de bord possible

Idéale pour les traitements de données volumineuses Idéal pour le développement des applications mono-thread

Lisibilité du code sur les longs programmes Code moins lisible lorsque l’application deviens vaste

Compréhension difficile pour les non-initiés Compréhension abordable pour tous

Moins performant dans certains cas S’adapte plus facilement à l’environnement

Plus adaptée au milieu universitaire et de traitement de


Plus adapté aux industries généralistes
données
𝜆 : Avantages factuels
Les programmes n’ont pas d’état
Idéal pour la parallélisation

Code facilement testable


Code facilement vérifiable
Code plus précis et concis

Peut se combiner avec une programmation impérative et orientée objet


𝜆 : Mon avis
Reste difficile d’accès
▪ Surtout si on a appris avec l’impératif, puis l’objet : paradigme orthogonal
▪ Code concis mais lecture difficile !
▪ Vrai pseudo code fonctionnel : sans commentaire, incompréhensible !

𝑠𝑜𝑙𝑣𝑒𝑟 𝐴 ∶ 𝑌, 𝑚 ∶ 𝑋, 𝑋 → 𝑋, ℎ ∶ 𝑌 → 𝑌, 𝑡 ∶ 𝑌 → 𝑌, 𝑒 ∶ 𝑌 → 𝑌, 𝑌 → 𝑏𝑜𝑜𝑙, 𝑝 ∶ 𝑌 → 𝑋 :
if e(t, A) : 𝑟𝑒𝑡𝑢𝑟𝑛 𝑚(𝑝(ℎ 𝐴 ), 𝑠𝑜𝑙𝑣𝑒𝑟 𝑡 𝐴 , 𝑚, ℎ, 𝑡, 𝑒, 𝑝 )
𝑒𝑙𝑠𝑒 ∶ 𝑟𝑒𝑡𝑢𝑟𝑛 𝑝(ℎ 𝐴 )

λ très efficace pour les pipelines ou graphes de traitement de données


𝜆 très efficace pour écrire des compilateurs ou transpileurs
▪ Langage (déclaratif) dont l’évaluation donne un code
▪ Mais qui fait ça à part des chercheurs ?!
𝜆 : Tendance mon avis
𝜆 remplace les functors

Généralisation des paramètres const ← vers de vraies fonctions

Objets immutables

Traitement des collections


▪ Map, filter, reduce, grow ← fin des for sémantiquement inutiles

Évaluation partielle ?
Tout est dit !
Rémi Cozot – remi.cozot@univ-littoral.fr
Version 2023 – 2024

Vous aimerez peut-être aussi