IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

JavaScript Discussion :

Functional programming et function factories (closure et binding arguments)


Sujet :

JavaScript

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre actif
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2013
    Messages
    54
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2013
    Messages : 54
    Par défaut Functional programming et function factories (closure et binding arguments)
    Bonjour, Bonsoir,

    Tjrs sur ma lancée d’étudier JavaScript, je suis entrain de lire un bouquin de Dan Mantyla - Functional Programming
    in JavaScript



    Dans ce livre, l'auteur colle un bout de code que je ne comprend absolument pas!!

    La fonction qui retourne une fonction qui retourne une fonction.. j'avoue que ca me dépasse complétement..
    De plus, la fonction est stockée dans une variable, qui du coup peut passer un argument "par après".. comment dire.. est ce que j'ai déjà dit que ça me dépassait complétement ??!!

    Je comprend que func(a, b) fait appel a [c]math.pow(a, b)[/]
    Je peux aussi suivre le cheminement des arguments/paramètres..
    Mais pourquoi et comment se fait-il que powersOfTwo fasse appel a return function (b) qui du coup renvoie return func(a, b)??

    Est ce que qlq'un aurait la gentillesse de m'aider a comprendre ce qu'il se passe ici?? Ou me guider sur un (très bon) article ??
    Je pensais avoir compris ce que c’était que les closure.. apparemment pas :/

    Function factories
    Remember our section on closures in Chapter 2, Fundamentals of Functional
    Programming? Closures are the constructs that makes it possible to create a useful
    JavaScript programming pattern known as function factories. They allow us to
    manually bind arguments to functions.
    First, we'll need a function that binds an argument to another function:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
     
    function bindFirstArg(func, a) {
      return function (b) {
        return func(a, b);
      };
    }
     
    // Then we can use this to create more generic functions:
    var powersOfTwo = bindFirstArg(Math.pow, 2);
    console.log(powersOfTwo(3)); // 8
    console.log(powersOfTwo(5)); // 32
     
    // And it can work on the other argument too:
    function bindSecondArg(func, b) {
      return function (a) {
        return func(a, b);
      };
    }
     
    var squareOf = bindSecondArg(Math.pow, 2);
    var cubeOf = bindSecondArg(Math.pow, 3);
     
    console.log(squareOf(3)); // 9
    console.log(squareOf(4)); // 16
    console.log(cubeOf(3)); // 27
    console.log(cubeOf(4)); // 64
    ps : désoler si le titre du sujet n'est pas très parlant.. je ne savais vraiment pas quoi mettre :/
    je le changerais si besoin est..

  2. #2
    Membre Expert
    Avatar de Doksuri
    Profil pro
    Développeur Web
    Inscrit en
    Juin 2006
    Messages
    2 494
    Détails du profil
    Informations personnelles :
    Âge : 55
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 494
    Par défaut
    developer.mozilla.org/fr/docs/Web/JavaScript/Closures
    peut-etre que l'explication de la doc te semblera plus claire
    La forme des pyramides prouve que l'Homme a toujours tendance a en faire de moins en moins.

    Venez discuter sur le Chat de Développez !

  3. #3
    Rédacteur

    Avatar de danielhagnoul
    Homme Profil pro
    Étudiant perpétuel
    Inscrit en
    Février 2009
    Messages
    6 389
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 74
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant perpétuel
    Secteur : Enseignement

    Informations forums :
    Inscription : Février 2009
    Messages : 6 389
    Billets dans le blog
    125
    Par défaut


    Présentation plus explicite, de mon point de vue, peut-être que cela vous aidera. Voir la console, touche F12.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    34
        /*
         * construction de fonctions qui prennent une fonction 
         * en premier argument et la retourne à l'envoyeur
         */
     
        function bindFirstArg(func, a) {
            return function (b) {
                console.log(`bindFirstArg : ${ func }, ${ a }, ${ b }`);
                return func(a, b);
            };
        }
     
        function bindSecondArg(func, b) {
            return function (a) {
                console.log(`bindSecondArg : ${ func }, ${ a }, ${ b }`);
                return func(a, b);
            };
        }
     
        /*
         * constructions de fonctions "indirectes"
         */
        let
            func = Math.pow,
            powersOfTwo = bindFirstArg(func, 2),
            squareOf = bindSecondArg(func, 2),
            cubeOf = bindSecondArg(func, 3);
     
        console.log(`Math.pow(2, 3) = ${ powersOfTwo(3) }`); // 8
        console.log(`Math.pow(2, 5) = ${ powersOfTwo(5) }`); // 32
        console.log(`Math.pow(3, 2) = ${ squareOf(3) }`); // 9
        console.log(`Math.pow(4, 2) = ${ squareOf(4) }`); // 16
        console.log(`Math.pow(3, 3) = ${ cubeOf(3) }`); // 27
        console.log(`Math.pow(4, 3) = ${ cubeOf(4) }`); // 64

    Blog

    Sans l'analyse et la conception, la programmation est l'art d'ajouter des bogues à un fichier texte vide.
    (Louis Srygley : Without requirements or design, programming is the art of adding bugs to an empty text file.)

  4. #4
    Expert confirmé
    Avatar de Watilin
    Homme Profil pro
    En recherche d'emploi
    Inscrit en
    Juin 2010
    Messages
    3 094
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : En recherche d'emploi

    Informations forums :
    Inscription : Juin 2010
    Messages : 3 094
    Par défaut
    Je vais essayer d’expliquer avec mes propres mots. Pas sûr que ça aide, mais je tente

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    function bindFirstArg(func, a) {
      return function (b) {
        return func(a, b);
      };
    }
    Ce qu’on doit remarquer en premier avec cette fonction bindFirstArg, c’est qu’elle retourne une fonction. On a donc une « indirectitude », dans le sens où elle ne renvoie pas directement le résultat d’un calcul, mais va renvoyer quelque chose qui permet d’avoir ce résultat. Ce quelque chose c’est une autre fonction, qui a été crée à l’intérieur de bindFirstArg, elle a donc un petit truc spécial : elle bénéficie du mécanisme des closures. Comme tu l’as lu, les closures permettent de « stocker » des valeurs en attendant de les utiliser.

    Pour prendre l’exemple le plus simple possible, choisissons l’addition :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    function add(x, y) {
      return x + y;
    }

    Que se passe-t-il quand on passe cette fonction à bindFirstArg ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    let add42 = bindFirstArg(add, 42)
    Tout se passe comme si on avait déclaré cette fonction :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    function add42(b) {
      return add(42, b);
    }
    Le 42, c’est la valeur qui a été « stockée » par la closure, et qui correspondait au paramètre a. Je rappelle qu’en JS, les paramètres et les variables locales, c’est pareil.

    À présent on a une nouvelle fonction qui accepte 1 argument :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    add42(1) // 43
    add42(7) // 49
    add42(8000) // 8042
    Ce qui peut rendre les choses plus confuses, c’est que la syntaxe JS permet de tout écrire en une seule ligne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    bindFirstArg(add, 42)(1) // 43
    C’est équivalent à :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    let tempFunc = bindFirstArg(add, 42);
    tempFunc(1) // 43
    (Sauf qu’il n’y a pas de tempFunc dans la portée locale.)

    Le second exemple, bindSecondArg, est juste le symétrique du premier, il ne présente pas de nouveau concept.

    Pour ce que ça vaut, c’est une technique qui s’appelle la currification (en anglais currying), du nom du mathématicien Haskell Curry. Il y a un article Wikipédia mais je le trouve peu accessible, il est truffé de jargon et de notations mathématiques. Cela dit, il y a un exemple JavaScript qui ressemble un peu à ce que je viens de montrer, peut-être que ça peut t’aider.
    La FAQ JavaScript – Les cours JavaScript
    Touche F12 = la console → l’outil indispensable pour développer en JavaScript !

Discussions similaires

  1. [Haskell] Craft of functional programming
    Par Thierry Chappuis dans le forum Haskell
    Réponses: 62
    Dernier message: 28/01/2008, 03h27
  2. anonymous functions et inline functions
    Par zax469 dans le forum MATLAB
    Réponses: 2
    Dernier message: 21/09/2007, 17h43
  3. Réponses: 2
    Dernier message: 26/06/2007, 20h31

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo