Académique Documents
Professionnel Documents
Culture Documents
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 →
// namespaces
open System
// 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
// 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 !
// 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 !
// 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 !
// 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]}"""
// 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
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
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
// 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
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
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)
// 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 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)
// 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 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
// alias de type
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
| 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 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
class Exp :
def Eval(self: Exp, env: dict[str, int]) -> int : ...
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)}")
Utiliser la récursivité pour les itérations Utilise les boucles pour les itérations
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
𝑠𝑜𝑙𝑣𝑒𝑟 𝐴 ∶ 𝑌, 𝑚 ∶ 𝑋, 𝑋 → 𝑋, ℎ ∶ 𝑌 → 𝑌, 𝑡 ∶ 𝑌 → 𝑌, 𝑒 ∶ 𝑌 → 𝑌, 𝑌 → 𝑏𝑜𝑜𝑙, 𝑝 ∶ 𝑌 → 𝑋 :
if e(t, A) : 𝑟𝑒𝑡𝑢𝑟𝑛 𝑚(𝑝(ℎ 𝐴 ), 𝑠𝑜𝑙𝑣𝑒𝑟 𝑡 𝐴 , 𝑚, ℎ, 𝑡, 𝑒, 𝑝 )
𝑒𝑙𝑠𝑒 ∶ 𝑟𝑒𝑡𝑢𝑟𝑛 𝑝(ℎ 𝐴 )
Objets immutables
Évaluation partielle ?
Tout est dit !
Rémi Cozot – remi.cozot@univ-littoral.fr
Version 2023 – 2024