Skip to content
Snippets Groups Projects
Verified Commit 453dfaba authored by orestis.malaspin's avatar orestis.malaspin
Browse files

added 2025

parent ece6d944
Branches
No related tags found
No related merge requests found
Pipeline #40290 passed
---
title: "Fonctions d'ordre supérieur"
date: "2025-05-16"
---
# Tribute
Rendons à Cesar:
* Ces slides ont été écrits par Michaël El Kharroubi
* J'arrive pas à changer l'auteur simplement sur un slide donc....
* Merci à lui pour ses efforts et qu'il soit crédité comme il se doit!
# Présentation du problème
* Imaginons que nous ayons la structure d'un vecteur en 3 dimensions suivante
```C
typedef struct _vec3 {
double x;
double y;
double z;
} vec3;
```
* On souhaite implémenter 3 opérations différentes
* La somme
* La soustraction
* Le produit de Hadamard (produit composantes à composantes)
# Présentation du problème (suite)
On a donc les fonctions suivantes
* Addition
```c
vec3 add(vec3 lhs, vec3 rhs){
vec3 res;
res.x = lhs.x + rhs.x;
res.y = lhs.y + rhs.y;
res.z = lhs.z + rhs.z;
return res;
}
```
# Présentation du problème (suite)
* Soustraction
```c
vec3 sub(vec3 lhs, vec3 rhs){
vec3 res;
res.x = lhs.x - rhs.x;
res.y = lhs.y - rhs.y;
res.z = lhs.z - rhs.z;
return res;
}
```
# Présentation du problème (suite)
* Produit de Hadamard
```c
vec3 mul(vec3 lhs, vec3 rhs){
vec3 res;
res.x = lhs.x * rhs.x;
res.y = lhs.y * rhs.y;
res.z = lhs.z * rhs.z;
return res;
}
```
* Quel est le problème avec ces trois fonctions?
# Présentation du problème (suite)
* Le problème avec ces fonctions c'est la **répétition**.
* La seule chose qui change, c'est l'opérateur (+,-,*).
* Problèmes possibles
* Tentation de copier-coller du code (donc risque d'erreurs)
* Faible résilience au changement (imaginons que je veuille des vecteurs 2d, 4d, nd)
# Présentation du problème (solution)
* Vecteur de taille dynamique
```c
typedef struct _vecn {
int size;
double *xs;
} vecn;
```
* Règle le problème de résilience du code, mais ne règle pas le problème de répétition...
# Fonction d'ordre supérieur (solution au problème)
* Pour notre problème, nous aimerions donc découpler l'opération (opération entre deux termes : +,-,*) de l'itération sur les composantes.
* Ce qui nous donne conceptuellement en pseudo c
```c
// Attention pseudo c, ne compile pas !!!!!
vec3 apply_operator(operator op, vec3 lhs, vec3 rhs){
vec3 res;
res.x = lhs.x op rhs.x;
res.y = lhs.y op rhs.y;
res.z = lhs.z op rhs.z;
return res;
}
```
# Fonction d'ordre supérieur (solution au problème)
* Avec notre fonction conceptuelle `apply_operator`, on pourrait faire (toujours en pseudo c)
```c
// Attention pseudo c, ne compile pas !!!!!
vec3 add(vec3 lhs, vec3 rhs){
return apply_operator(+, lhs, rhs);
}
vec3 sub(vec3 lhs, vec3 rhs){
return apply_operator(-, lhs, rhs);
}
vec3 mul(vec3 lhs, vec3 rhs){
return apply_operator(*, lhs, rhs);
}
```
* En fait, on vient de créer ce qu'on appelle une fonction d'ordre supérieur.
# Fonction d'ordre supérieur (définition)
* Une fonction d'ordre supérieur est une fonction qui prend en paramètre et/ou retourne une(des) autre(s) fonction(s).
* Si on essayait de définir `operator`, c'est en fait une fonction qui prend deux paramètres (un terme de gauche et un terme de droite). On s'en aperçoit clairement avec la notation préfix (polonaise).
* `L + R -> + L R`
* `L - R -> - L R`
* `L * R -> * L R`
* Comment l'implémenter concrètement en C?
# Implémentation
* Si on reprend la signature de notre fonction d'exemple, on a
```c
vec3 apply_operator(operator op, vec3 lhs, vec3 rhs);
```
* Nous avons déterminé que les `operator` étaient des fonctions qui prennaient deux paramètres.
* Pour passer une fonction en paramètre en C, nous devons la passer par référence, c'est à dire à l'aide d'un pointeur de fonction.
# Pointeur de fonctions
* Un pointeur de fonction se définit ainsi
```c
<type retour> (*<nom ptr fonc>)(<type params(s)>);
```
* Ou encore avec un `typedef`
```c
typedef <type retour> (*<nom ptr fonc>)(<type params(s)>);
```
* Dans notre cas, nous avons donc un type de fonction nommé `operator`, qui prend en entrée deux `double`{.c} et qui retourne un `double`{.c}. Ce qui nous donne
```c
typedef double (*operator)(double, double);
```
# Implémentation (suite)
* En reprenant notre fonction `apply_operator`, on a donc
```c
vec3 apply_operator(operator op, vec3 lhs, vec3 rhs){
vec3 res;
res.x = op(lhs.x, rhs.x);
res.y = op(lhs.y, rhs.y);
res.z = op(lhs.z, rhs.z);
return res;
}
```
* NB : On voit que pour appeler notre fonction passée en paramètre, nous avons pu le faire comme avec n'importe quelle fonction.
# Résultat
```c
typedef double (*operator)(double, double);
vec3 apply_operator(operator op, vec3 lhs, vec3 rhs){
vec3 res;
res.x = op(lhs.x, rhs.x);
res.y = op(lhs.y, rhs.y);
res.z = op(lhs.z, rhs.z);
return res;
}
double add_dbl(double lhs, double rhs){
return lhs + rhs;
}
vec3 add(vec3 lhs, vec3 rhs){
return apply_operator(add_dbl, lhs, rhs);
}
```
# Fonctions d'ordre supérieur appliquées aux tableaux
* Comment appliquer des opérations sur un vecteur de taille n?
* Map (application d'une fonction)
* `add_one`, `square`
* Filter (discrimination selon un prédicat)
* `is_even`, `is_lower_than_five`
* Reduce (réduction d'un vecteur à un seul élément)
* `sum`, `multiply`
# Le map
* Exemple d'application
```c
typedef double (*operator)(double);
double *map(operator op, double *tab, size_t size) {
double *res = malloc(sizeof(*res) * size);
for (int i = 0; i < size; ++i) {
res[i] = op(tab[i]);
}
return res;
}
double add_one(double val) {
return val + 1;
}
double sqr(double val){
return val * val;
}
double tab[] = {1.0, 2.0, 3.0};
double *square = map(sqr, tab, 3);
double *and_one = map(add_one, square, 3);
```
# Le map
* Permettrait le chaînage.
```C
double *sqr_and_one = map(add_one, map(sqr, tab, 3), 3);
```
. . .
* Problème?
. . .
* Allocation dynamique... fuite mémoire.
. . .
* Solution?
. . .
```c
typedef double (*operator)(double);
double *map(operator op, double *tab, size_t size) {
double *res = malloc(sizeof(*res) * size);
for (int i = 0; i < size; ++i) {
res[i] = op(tab[i]);
}
free(tab);
return res;
}
```
* Problème potentiel?
* **Attention au double free!**
---
title: "La généricité"
date: "2025-05-16"
---
# Problématique
* En C on doit écrire chaque algorithme/structures de données pour des types
précis (`int`, `double`, `char`, ...).
```
void int_sort(int size, int tab[size]); // tri d'entiers
void double_sort(int size, int tab[size]); // tri de double
void char_sort(int size, char tab[size]); // tri de char
```
* Duplication du code pour chaque type possible et imaginable.
* On aimerait un moyen pour pouvoir représenter "n'importe quel type" sans
réécrire tout le code.
# La généricité
## Une "solution": `void *`{.C}
* En général, un pointeur connaît son **adresse** et le **type** des données sur lesquelles il pointe.
```C
int *a = malloc(sizeof(*a));
int *b = malloc(sizeof(int));
```
* Un `void *`{.C} le connaît **que** son adresse, au programmeur de pas faire n'importe quoi.
* Vous avez déjà utilisé des fonctions utilisant des `void *`{.C}
```C
void *malloc(size_t size);
void free(void *);
```
# Attention danger
* Ne permet pas au compilateur de vérifier les types.
* Les données pointées n'ayant pas de type, il faut déréférencer avec précaution:
```C
int a = 2;
void *b = &a; //jusqu'ici tout va bien
double c = *b; // argl!
```
* Une attention accrue est nécessaire.
# Cas particuier: on sait pas comment libérer la mémoire
## Exemple
```C
struct tab {
int *t;
}
struct tab *tmp = malloc(sizeof(*tmp));
tmp->t = malloc(10 * sizeof(*(tmp->t)));
free(tmp); // memory leak of tmp->t...
```
. . .
## Solution: tout faire à la main
```C
free(tmp->t);
free(tmp);
```
# Exemple simple
* On souhaite échanger deux pointeurs
```C
int *a = malloc();
int *b = malloc();
swap(&a, &b);
```
* Comment écrire `swap()` pour que le code ci-dessus marche pour n'importe quel
type?
. . .
```C
void swap(void **a, void **b) {
void *tmp = *a;
*a = *b;
*b = tmp;
}
```
# Cas d'utilisation (1/4)
\footnotesize
* La somme d'un tableau de type arbitraire (facile non?)
```C
void sum(void *tab, int length, size_t size_elem, void *zero,
void (*add)(void *, void *)) {
for (int i = 0; i < length; ++i) {
void *rhs = (void *)((char *)tab + i * size_elem);
add(zero, rhs);
} // de combien on "saute" avec un void *?
}
```
* Pour des entiers
```C
void int_add(void *lhs, void *rhs) {
*((int *)lhs) += *((int *)rhs); // cast d'entiers
}
int zero = 0;
int tab[] = {1, -2, 4, 5};
sum(tab, 4, sizeof(int), &zero, int_add);
printf("%d\n", zero);
```
# Cas d'utilisation (2/4)
## Que fait cette fonction?
\footnotesize
```C
void *foo(void *tab, int n_items, int s_items,
bool (*bar)(void *, void *)) {
if (n_items <= 0 || s_items <= 0 || NULL == tab) {
return NULL;
}
void *elem = tab;
for (int i = 1; i < n_items; ++i) {
// void pointer arithmetics is illegal in C
// (gcc is ok though)
void *tmp_elem = (void *)((char *)tab + i*s_items);
if (bar(elem, tmp_elem)) {
elem = tmp_elem;
}
}
return elem;
}
```
# Cas d'utilisation (3/4)
## Avec un tableau de `int`{.C}
```C
bool cmp_int(void *a, void *b) {
return (*(int *)a < *(int *)b);
}
int main() {
int tab[] = {-1, 2, 10, 3, 8};
int *a = foo(tab, 5, sizeof(int), cmp_int);
printf("a = %d\n", *a);
}
```
# Cas d'utilisation (4/4)
## Avec un tableau de `double`{.C}
```C
bool cmp_dbl(void *a, void *b) {
return (*(double *)a < *(double *)b);
}
int main() {
double tab[] = {-1.2, 2.1, 10.5, 3.6, 18.1};
double *a = foo(tab, 5, sizeof(double), cmp_dbl);
printf("a = %f\n", *a);
}
```
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment