Précédent   Forum du club des développeurs et IT Pro > Autres langages > Langages fonctionnels
Langages fonctionnels Forum d'entraide sur la programmation en langages fonctionnels : Lisp, Scheme, Caml, Haskell, Erlang, Oz, Anubis, ...
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse
 
Outils de la discussion
Publicité
'
Vieux 27/02/2011, 12h35   #1
Ubiquité
Membre éprouvé
 
Inscription : avril 2006
Messages : 422
Détails du profil
Informations forums :
Inscription : avril 2006
Messages : 422
Points : 442
Points : 442
Envoyer un message via MSN à Ubiquité
Par défaut Pointeur de fonction en C

Est-ce que l'existance des pointeurs de fonction en C en fait un language fonctionnel ?
Si non, je serai curieux de savoir pourquoi.
Ubiquité est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 27/02/2011, 15h51   #2
gasche
Membre Expert
 
Inscription : avril 2007
Messages : 829
Détails du profil
Informations forums :
Inscription : avril 2007
Messages : 829
Points : 1 007
Points : 1 007
Il n'y pas de définition stricte et formelle de ce qu'est un "langage fonctionnel". Il y a des outils utiles pour faire de la programmation fonctionnelle, mais souvent c'est aussi une convention "sociale", les langages dans lequel le style fonctionnel est encouragé.

Un critère objectif est le fait d'avoir ce qu'on appelle des "fonctions d'ordre supérieur". Un langage a les fonctions d'ordre supérieur quand les fonctions sont des valeurs à part entière du langage, c'est-à-dire qu'elles peuvent être reçues en arguments, renvoyées comme valeur de retour, et construites à tout moment de l'évaluation.

Les pointeurs de fonctions en C, à ma connaissance, ne remplissent pas tout à fait ce cahier des charges : on peut passer et renvoyer des adresses de fonctions existantes, mais ces fonctions sont créées "statiquement" dans le programme et ne peuvent pas être générées dynamiquement pendant l'exécution. Peut-être que les extensions pour les fonctions imbriquées et les fermetures/closures répondent à ce besoin, je ne sais pas.

On peut se passer de la nécessité de créer des fonctions dynamiquement en appliquant des transformations globales aux programmes qui "rendent statiques" toutes les fonctions (closure conversion, défonctionalisation...). C'est ce que font souvent les compilateurs de langages fonctionnels. Si tu as un programme utilisant des fonctions d'ordre-supérieur en tête, tu peux donc en écrire une forme transformée en C. Mais ce n'est pas satisfaisant car cette transformation globale rend le code moins maintenable et modulaire (une transformation qui était seulement locale dans le langage fonctionnel imaginaire peut te demander des modifications à de nombreux endroits dans un code C traduit).
gasche est déconnecté   Envoyer un message privé Réponse avec citation 10
Vieux 27/02/2011, 19h39   #3
Ubiquité
Membre éprouvé
 
Inscription : avril 2006
Messages : 422
Détails du profil
Informations forums :
Inscription : avril 2006
Messages : 422
Points : 442
Points : 442
Envoyer un message via MSN à Ubiquité
Mais d'aprés wikipédia, une fonction d'ordre supérieur c'est plus simple que ça :
Il suffit qu'une fonction soit capable de prendre comme paramétre une autre fonction ou bien d'en retourner une. Et ça grace au pointeur de fonction le C en est capable.
Mais effectivement on peut pas générer dynamiquement une fonction en C.*




*J'ai pour le moment pratiquement rien écrit dans un language fonctionnel, et j'arrive pas du tout à concevoir comment on peut générer dynamiquement une fonction ^^
Vivement que j'en connaisse un peu plus.
Ubiquité est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 27/02/2011, 22h58   #4
gasche
Membre Expert
 
Inscription : avril 2007
Messages : 829
Détails du profil
Informations forums :
Inscription : avril 2007
Messages : 829
Points : 1 007
Points : 1 007
Tu as raison, j'ai fait une légère confusion de langage : prendre et retourner c'est "ordre supérieur" et "manipuler comme toute autre valeur" (donc ça comprend aussi la création) c'est "fonctions de première classe" (plus généralement "X de première classe" quand X est une valeur comme les autres; on peut par exemple considérer les références OCaml comme des "lvalues de première classe").


Au sujet de la création dynamique de fonctions : quand on fait de la programmation fonctionnelle on passe son temps à construire des fonctions. Par exemple, comment traduirais-tu en C le programme OCaml suivant ? L'important n'est pas ici ce qu'il fait (il calcule 1 + 2 + 3, facile), mais comment il le fait, en utilisant des fonctions.

Code :
1
2
3
4
5
6
7
let compose f g = fun x -> f (g x)
let ajoute = fun a -> fun b -> a + b

let test =
  let f = compose (ajoute 1) (ajoute 2) in
  f 3
gasche est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/02/2011, 02h42   #5
Ubiquité
Membre éprouvé
 
Inscription : avril 2006
Messages : 422
Détails du profil
Informations forums :
Inscription : avril 2006
Messages : 422
Points : 442
Points : 442
Envoyer un message via MSN à Ubiquité
Citation:
Envoyé par bluestorm Voir le message
Tu as raison, j'ai fait une légère confusion de langage : prendre et retourner c'est "ordre supérieur" et "manipuler comme toute autre valeur" (donc ça comprend aussi la création) c'est "fonctions de première classe" (plus généralement "X de première classe" quand X est une valeur comme les autres; on peut par exemple considérer les références OCaml comme des "lvalues de première classe").


Au sujet de la création dynamique de fonctions : quand on fait de la programmation fonctionnelle on passe son temps à construire des fonctions. Par exemple, comment traduirais-tu en C le programme OCaml suivant ? L'important n'est pas ici ce qu'il fait (il calcule 1 + 2 + 3, facile), mais comment il le fait, en utilisant des fonctions.

Code :
1
2
3
4
5
6
7
let compose f g = fun x -> f (g x)
let ajoute = fun a -> fun b -> a + b

let test =
  let f = compose (ajoute 1) (ajoute 2) in
  f 3

J'essaye de comprendre exactement comment il le fait pour essayer de refaire la même chose en C mais je vois pas
Ubiquité est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/02/2011, 05h27   #6
Mat.M
Expert Confirmé Sénior
 
Développeur informatique
Inscription : novembre 2006
Messages : 4 445
Détails du profil
Informations personnelles :
Localisation : France, Rhône (Rhône Alpes)

Informations professionnelles :
Activité : Développeur informatique

Informations forums :
Inscription : novembre 2006
Messages : 4 445
Points : 5 864
Points : 5 864
Citation:
Envoyé par Ubiquité Voir le message
Mais effectivement on peut pas générer dynamiquement une fonction en C.*

et j'arrive pas du tout à concevoir comment on peut générer dynamiquement une fonction ^^
qu'appelle tu "gérer dynamiquement une fonction en C" ?
Cela n'est pas possible le langage C est un langage compilé qui donne au final du code machine
Mat.M est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/02/2011, 08h52   #7
gasche
Membre Expert
 
Inscription : avril 2007
Messages : 829
Détails du profil
Informations forums :
Inscription : avril 2007
Messages : 829
Points : 1 007
Points : 1 007
Le langage OCaml, dans lequel j'ai donné cet exemple, est aussi compilé vers du code machine. Le compilateur ne fait pas du JIT pour les déclarations de fonction (compiler le code de la fonction pendant l'exécution), ce serait bien trop coûteux: il utilise des techniques de traduction pour éliminer les fonctions "dynamiques" (les fonctions, anonymes ou non, qui ne sont pas au toplevel).

Ubiquité : tu ne comprends pas le sens du code ((fun x -> foo) est une fonction anonyme qui renvoie (foo) quand on lui donne la valeur de (x)), ou tu ne comprends pas comment il est exécuté ?

Syntaxiquement le code a le comportement suivant :

let f = compose (ajoute 1) (ajoute 2) in f 3
=> let f = compose (fun b -> 1 + b) (fun c -> 2 + c) in f 3
=> let f = fun x -> (fun b -> 1 + b) ((fun c -> 2 + c) x) in f 3
=> (fun x -> (fun b -> 1 + b) ((fun c -> 2 + c) x)) 3
=> (fun b -> 1 + b) ((fun c -> 2 + c) 3)
=> (fun b -> 1 + b) (2 + 3)
=> (fun b -> 1 + b) 5
=> 1 + 5
=> 6
gasche est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/02/2011, 13h27   #8
Ubiquité
Membre éprouvé
 
Inscription : avril 2006
Messages : 422
Détails du profil
Informations forums :
Inscription : avril 2006
Messages : 422
Points : 442
Points : 442
Envoyer un message via MSN à Ubiquité
Citation:
Envoyé par bluestorm Voir le message
Ubiquité : tu ne comprends pas le sens du code ((fun x -> foo) est une fonction anonyme qui renvoie (foo) quand on lui donne la valeur de (x)), ou tu ne comprends pas comment il est exécuté ?

Syntaxiquement le code a le comportement suivant :

let f = compose (ajoute 1) (ajoute 2) in f 3
=> let f = compose (fun b -> 1 + b) (fun c -> 2 + c) in f 3
=> let f = fun x -> (fun b -> 1 + b) ((fun c -> 2 + c) x) in f 3
=> (fun x -> (fun b -> 1 + b) ((fun c -> 2 + c) x)) 3
=> (fun b -> 1 + b) ((fun c -> 2 + c) 3)
=> (fun b -> 1 + b) (2 + 3)
=> (fun b -> 1 + b) 5
=> 1 + 5
=> 6
Je comprend pas le sens du code.
in f 3 ca veut dire quoi ?
Ubiquité est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/02/2011, 14h26   #9
gasche
Membre Expert
 
Inscription : avril 2007
Messages : 829
Détails du profil
Informations forums :
Inscription : avril 2007
Messages : 829
Points : 1 007
Points : 1 007
"let x = a in b" c'est la forme des déclarations locale en Caml, ça veut dire "déclare la variable x comme ayant la valeur de a, et évalue b (qui peut utiliser x)".
Tu connais un autre langage fonctionnel (ou un autre langage qui a au moins des fonctions de première classe) dans lequel je pourrais fournir l'exemple ?


En Javascript:
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function compose(f, g) {
    return function(x) {
        return f(g(x));
    }
}

function addition(a) {
    return function(b) {
        return a + b;
    }
}

var f = compose(addition(1), addition(2));
var test = f(3);
gasche est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/02/2011, 16h13   #10
Ubiquité
Membre éprouvé
 
Inscription : avril 2006
Messages : 422
Détails du profil
Informations forums :
Inscription : avril 2006
Messages : 422
Points : 442
Points : 442
Envoyer un message via MSN à Ubiquité
Je galere pour faire ca en C mais en C++ avec les foncteurs ca doit être bien plus faisable.

Déjà la fonction addition pen C++ peut être un foncteur comme celui là :

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Addition {
private:
  double a;
public:
  Addition (double _a)
    :a(_a)
  {
  }

  double operator()(double _b)
  {
    return _b + a;
  }
}
Ubiquité est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 05/03/2011, 16h50   #11
TropMDR
Membre chevronné
 
Inscription : mars 2010
Messages : 281
Détails du profil
Informations forums :
Inscription : mars 2010
Messages : 281
Points : 752
Points : 752
C et C++ dans leur version actuelle ne sont pas du tout des "langages fonctionnels" parce qu'ils ne permettent pas de capture de variable lors de la définition des fonctions (c'est ça qui permet de "créer dynamiquement" des fonctions, appelées aussi "fermetures").

Prenons un exemple de ce que permettrait la capture en C (en fait, de ce qu'elle permet, grace à l'extension "block" d'apple)
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/* Exemple tiré de Wikipedia*/
#include <stdio.h>
#include <Block.h>
typedef int (^IntBlock)();
 
IntBlock MakeCounter(int start, int increment) {
        __block int i = start;
 
        return Block_copy( ^ {
                int ret = i;
                i += increment;
                return ret;
        });
 
}
 
int main(void) {
        IntBlock mycounter = MakeCounter(5, 2);
        printf("First call: %d\n", mycounter());
        printf("Second call: %d\n", mycounter());
        printf("Third call: %d\n", mycounter());
 
        /* because it was copied, it must also be released */
        Block_release(mycounter);
 
        return 0;
}
/* Output:
        First call: 5
        Second call: 7
        Third call: 9
*/
L'idée est que dans la fonction MakeCounter, on "crée" une nouvelle fonction
Code :
1
2
3
4
5
        return Block_copy( ^ {
                int ret = i;
                i += increment;
                return ret;
        });
Cette fonction utilise deux variables, i et increment, qui ne sont pas des variables globales, et qui ne sont pas définis directement dans la fonction. Ce sont des variables qui ont été "capturées" depuis la fonction MakeCounter.

En développant un peu cet exemple (et sans avoir testé), supposons que la fonction main deviennent

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main(void) {
        IntBlock mycounter52 = MakeCounter(5, 2);
        IntBlock mycounter11 = MakeCounter(1, 1);
        printf("First call of 5 2: %d\n", mycounter52());
        printf("First call of 1 1: %d\n", mycounter11());
        printf("Second call of 1 1: %d\n", mycounter11());
        printf("Second call of 5 2: %d\n", mycounter52());
 
        /* because it was copied, it must also be released */
        Block_release(mycounter52);
        Block_release(mycounter11);
 
        return 0;
}
Ca produirait la sortie
Code :
1
2
3
4
First call of 5 2: 5
First call of 1 1: 1
Second call of 1 1: 2
Second call of 5 2: 7
On voit qu'il y a bien deux fonctions différentes qui ont été produites, et qu'elles ne partagent pas leurs variables capturées.

Et attention, ici tous les paramètres sont connus, mais il n'y a pas deux fonctions créées lors de la compilation. On pourrait très bien imaginer un tableau de fonctions
Code :
1
2
3
4
5
6
int i;
IntBlock mycounters [10];
for(i = 0; i < 0; ++i){
  mycounters[i] = MakeCounter(1, i);
}
...
ou que ça dépendent de paramètres extérieurs, etc etc.

Tout ça est impossible à faire en C standard (on peut "tricher" en créant une structure contenant le pointeur de fonction et le tableau de ses variables capturées, puis avoir une macro d'application qui rajoute le tableau comme premier argument, ce que fait sans doute l'implémentation, mais ce n'est pas la même chose !). Ce sera possible en C++0x avec les "lambdas"

J'espère que ça aide avec une syntaxe moins "caml" (même si la syntaxe caml est quand même nettement plus adaptées :-D)
TropMDR est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 05/03/2011, 21h56   #12
Ubiquité
Membre éprouvé
 
Inscription : avril 2006
Messages : 422
Détails du profil
Informations forums :
Inscription : avril 2006
Messages : 422
Points : 442
Points : 442
Envoyer un message via MSN à Ubiquité
Donc finalement avoir des fonctions d'ordre supérieur ne suffit pas ? Les fonctions d'un langage doivent être un objet de première classe pour que le langage implement le paradigme fonctionnel ?


Et t'es sur que les foncteurs du c++ ne permettent pas la capture ?
Ubiquité est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 05/03/2011, 23h57   #13
gl
Rédacteur/Modérateur
 
Homme
Inscription : juin 2002
Messages : 2 034
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 34
Localisation : France, Hauts de Seine (Île de France)

Informations forums :
Inscription : juin 2002
Messages : 2 034
Points : 3 828
Points : 3 828
Citation:
Envoyé par Ubiquité Voir le message
Et t'es sur que les foncteurs du c++ ne permettent pas la capture ?
Non, les foncteurs ne capturent pas en C++.

On peut se rapprocher de ce comportement avec les foncteurs et leurs constructeurs mais il n'y a pas de capture.
gl est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 06/03/2011, 00h36   #14
gasche
Membre Expert
 
Inscription : avril 2007
Messages : 829
Détails du profil
Informations forums :
Inscription : avril 2007
Messages : 829
Points : 1 007
Points : 1 007
Citation:
Donc finalement avoir des fonctions d'ordre supérieur ne suffit pas ? Les fonctions d'un langage doivent être un objet de première classe pour que le langage implement le paradigme fonctionnel ?
Oui.

Si tu n'arrives pas à implémenter mon exemple dans un langage de programmation (en utilisant des fonctions), il peut difficilement se prétendre un langage fonctionnel.
gasche est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 06/03/2011, 01h43   #15
Ubiquité
Membre éprouvé
 
Inscription : avril 2006
Messages : 422
Détails du profil
Informations forums :
Inscription : avril 2006
Messages : 422
Points : 442
Points : 442
Envoyer un message via MSN à Ubiquité
Oki,
Merci pour toutes les précisions.
Quand je reviendrai je serai surement entrain de lire Real World Haskell.
Ubiquité est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 06/03/2011, 21h01   #16
LLB
Membre Expert
 
Inscription : mars 2002
Messages : 962
Détails du profil
Informations forums :
Inscription : mars 2002
Messages : 962
Points : 1 148
Points : 1 148
Pour info, les fonctions anonymes arrivent avec C++0x (et on choisit à chaque fois si la capture se fait par copie ou par référence).
LLB est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 11/03/2011, 16h51   #17
NokyDaOne
Membre régulier
 
Inscription : septembre 2007
Messages : 99
Détails du profil
Informations forums :
Inscription : septembre 2007
Messages : 99
Points : 78
Points : 78
Tous les langages savent manier les fonctions (sauf JAVA qui a décider de suivre la voie 100% objet) vu que l'assembleur sait le faire.

Ce qui fait réellement un langage fonctionnel, c'est l'application partielle. Dès que l'on a cette fonctionnalité on peut coder en fonctionnel, quand on ne l'a pas on ne peut pas coder en fonctionnel. Et dans ce domaine le C est un incapable. Ce n'est donc pas un langage fonctionnel.
NokyDaOne est déconnecté   Envoyer un message privé Réponse avec citation 01
Vieux 11/03/2011, 17h10   #18
gasche
Membre Expert
 
Inscription : avril 2007
Messages : 829
Détails du profil
Informations forums :
Inscription : avril 2007
Messages : 829
Points : 1 007
Points : 1 007
NokyDaOne > pardon ? L'assembleur "gère les fonctions" ? Comment ? Mais il ne "gère" pas l'application partielle ? Qu'est-ce que c'est ?
Ça ne veut pas dire grand chose ce que tu dis...
gasche est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 11/03/2011, 17h13   #19
NokyDaOne
Membre régulier
 
Inscription : septembre 2007
Messages : 99
Détails du profil
Informations forums :
Inscription : septembre 2007
Messages : 99
Points : 78
Points : 78
Citation:
Envoyé par bluestorm Voir le message
NokyDaOne > pardon ? L'assembleur "gère les fonctions" ? Comment ? Mais il ne "gère" pas l'application partielle ? Qu'est-ce que c'est ?
Ça ne veut pas dire grand chose ce que tu dis...
Ce que je voulais dire c'est que l'assembleur gère les pointeurs de fonction (c'est la raison pour laquelle le C sait le faire). Mais l'assembleur ne permet pas de faire d'application partielle (c'est la raison pour laquelle le C ne sait pas le faire).
Si, ce que je dis veut dire quelque-chose , je m'exprime mal, c'est tout.
NokyDaOne est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 11/03/2011, 17h37   #20
gorgonite
Rédacteur/Modérateur

 
Avatar de gorgonite
 
Homme Nicolas Vallée
Ingénieur d'études
Inscription : décembre 2005
Messages : 9 963
Détails du profil
Informations personnelles :
Nom : Homme Nicolas Vallée
Âge : 28
Localisation : France

Informations professionnelles :
Activité : Ingénieur d'études
Secteur : Transports

Informations forums :
Inscription : décembre 2005
Messages : 9 963
Points : 18 157
Points : 18 157
Citation:
Envoyé par NokyDaOne Voir le message
Ce que je voulais dire c'est que l'assembleur gère les pointeurs de fonction (c'est la raison pour laquelle le C sait le faire). Mais l'assembleur ne permet pas de faire d'application partielle (c'est la raison pour laquelle le C ne sait pas le faire).


euh c'est extrêmement réducteur... tu en arrives presque à dire que C = assembleur en terme de fonctionnalités
je sais bien que les aficionados français des langages fonctionnels sont plus proches de la famille ML, mais il ne faudrait tout de même pas dire que la famille Algol (et son représentant le plus représenté aujourd'hui : C) est limitée à l'assembleur...
__________________
Evitez les MP pour les questions techniques... il y a des forums
Contributions sur DVP : Mes Tutos | Mon Blog
gorgonite est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse
Outils de la discussion

Navigation rapide


Fuseau horaire GMT +2. Il est actuellement 02h44.


 
 
 
 
Partenaires

Hébergement Web