
En programmation informatique , un rappel est une fonction stockée sous forme de données (une référence ) et conçue pour être appelée par une autre fonction, souvent vers la couche d'abstraction d'origine .
Une fonction qui accepte un paramètre de rappel peut être conçue pour effectuer un rappel avant de retourner à son appelant, ce qui est connu sous le nom de fonction synchrone ou bloquante . La fonction qui accepte un rappel peut être conçue pour stocker le rappel afin qu'il puisse être rappelé après son retour, ce qui est connu sous le nom de fonction asynchrone , non bloquante ou différée .
Les langages de programmation prennent en charge les rappels de différentes manières, telles que les pointeurs de fonction , les expressions lambda et les blocs .
Analogie
Pour aider à comprendre le concept, voici une analogie tirée de la vie réelle.
Un client se rend dans un magasin pour passer une commande. C'est comme le premier appel.
Le client remet à un commis une liste d'articles, un chèque pour couvrir leur coût et des instructions de livraison. Tels sont les paramètres du premier appel incluant le rappel qui est les instructions de livraison. Il est entendu que le chèque sera encaissé et que les instructions seront suivies.
Lorsque le personnel le peut, il livre les articles comme indiqué, ce qui revient à appeler le rappel.
Il est à noter que la livraison n'a pas besoin d'être effectuée par le vendeur qui a pris la commande. Un rappel n'a pas besoin d'être appelé par la fonction qui a accepté le rappel comme paramètre.
De plus, il n'est pas nécessaire que la livraison soit effectuée directement au client. Un rappel n'a pas besoin d'être adressé à la fonction appelante. En fait, une fonction ne se présenterait généralement pas comme un rappel. Certains trouvent que l'utilisation de back est trompeuse puisque l'appel n'est (généralement) pas adressé à l'appelant d'origine comme c'est le cas pour un appel téléphonique .
Utiliser
Un rappel bloquant s'exécute dans le contexte d'exécution de la fonction qui transmet le rappel. Un rappel différé peut s'exécuter dans un contexte différent, par exemple pendant une interruption ou à partir d'un thread . En tant que tel, un rappel différé peut être utilisé pour la synchronisation et la délégation de travail à un autre thread.
Gestion des événements
Un rappel peut être utilisé pour la gestion des événements. Souvent, le code de consommation enregistre un rappel pour un type d'événement particulier. Lorsque cet événement se produit, le rappel est appelé.
Les rappels sont souvent utilisés pour programmer l' interface utilisateur graphique (GUI) d'un programme exécuté dans un système de fenêtrage . L'application fournit une référence à une fonction de rappel personnalisée que le système de fenêtrage doit appeler. Le système de fenêtrage appelle cette fonction pour notifier l'application d'événements tels que des clics de souris et des appuis sur des touches .
Action asynchrone
Un rappel peut être utilisé pour implémenter un traitement asynchrone. Un appelant demande une action et fournit un rappel à appeler lorsque l'action est terminée, ce qui peut se produire longtemps après la demande.
Polymorphisme
Un rappel peut être utilisé pour implémenter le polymorphisme . Dans le pseudo-code suivant, SayHipeut prendre soit WriteStatusou WriteError.
def WriteStatus ( chaîne message ): Écriture ( stdout , message ) def WriteError ( chaîne message ): Écriture ( stderr , message ) def SayHi ( écriture ): Écriture ( "Bonjour tout le monde" )
Action conditionnelle
Un rappel peut être utilisé pour implémenter un comportement conditionnel. Dans le pseudo-code suivant, si la journalisation est activée, Logappelle le rappel, getMessageet écrit le résultat. Mais, si la journalisation n'est pas activée, alors getMessagen'est pas appelé, ce qui permet d'économiser le coût d'exécution.
def Log ( getMessage ): if isLoggingEnabled : message = getMessage (); WriteLine ( message );
Mise en œuvre
La technologie de rappel est implémentée différemment selon le langage de programmation .
En assembleur , C , C++ , Pascal , Modula2 et d'autres langages, une fonction de rappel est stockée en interne sous forme de pointeur de fonction . L'utilisation du même stockage permet à différents langages de partager directement des rappels sans couche d'interopérabilité au moment de la conception ou de l'exécution . Par exemple, l' API Windows est accessible via plusieurs langages, compilateurs et assembleurs.
C++ permet également aux objets de fournir une implémentation de l'opération d'appel de fonction. La bibliothèque de modèles standard accepte ces objets (appelés foncteurs ) comme paramètres.
De nombreux langages dynamiques , tels que JavaScript , Lua , Python , Perl et PHP , permettent de transmettre un objet fonction.
Les langages CLI tels que C# et VB.NET fournissent une référence de fonction d'encapsulation de type sécurisé appelée délégué .
Les événements et les gestionnaires d'événements , tels qu'utilisés dans les langages .NET, fournissent des rappels.
Les langages fonctionnels prennent généralement en charge les fonctions de première classe , qui peuvent être transmises sous forme de rappels à d'autres fonctions, stockées sous forme de données ou renvoyées par des fonctions.
De nombreux langages, dont Perl, Python, Ruby , Smalltalk , C++ (11+), C# et VB.NET (nouvelles versions) et la plupart des langages fonctionnels, prennent en charge les expressions lambda , des fonctions sans nom avec une syntaxe en ligne, qui agissent généralement comme des rappels.
Dans certains langages, dont Scheme , ML , JavaScript, Perl, Python, Smalltalk, PHP (depuis 5.3.0), C++ (11+), Java (depuis 8), et bien d'autres, un lambda peut être une fermeture , c'est-à-dire qu'il peut accéder à des variables définies localement dans le contexte dans lequel le lambda est défini.
Dans un langage de programmation orienté objet tel que Java , les versions antérieures aux arguments à valeur de fonction permettent d'obtenir le comportement d'un rappel en passant un objet qui implémente une interface. Les méthodes de cet objet sont des rappels.
Dans PL/I et ALGOL 60, une procédure de rappel peut avoir besoin de pouvoir accéder à des variables locales dans des blocs conteneurs, elle est donc appelée via une variable d'entrée contenant à la fois le point d'entrée et les informations de contexte.
Exemple de code
C
Les rappels ont une grande variété d'utilisations, par exemple dans la signalisation d'erreurs : un programme Unix peut ne pas vouloir se terminer immédiatement lorsqu'il reçoit SIGTERM , donc pour s'assurer que sa fin est gérée correctement, il doit enregistrer la fonction de nettoyage comme un rappel. Les rappels peuvent également être utilisés pour contrôler si une fonction agit ou non : Xlib permet de spécifier des prédicats personnalisés pour déterminer si un programme souhaite gérer un événement.
Dans le code C suivant , la fonction PrintNumberutilise le paramètre getNumbercomme rappel de blocage. PrintNumberest appelée avec GetAnswerToMostImportantQuestionlaquelle agit comme une fonction de rappel. Lors de l'exécution, la sortie est : « Valeur : 42 ».
#include <stdio.h> #include <stdlib.h> void PrintNumber ( int ( * getNumber )( void )) { int val = getNumber (); printf ( "Valeur : %d " , val ); } int GetAnswerToMostImportantQuestion ( void ) { retour 42 ; } int main ( void ) { PrintNumber ( GetAnswerToMostImportantQuestion ); return 0 ; }
C++
En C++, le foncteur peut être utilisé en plus du pointeur de fonction.
C#
Dans le code C#Helper.Method suivant, la méthode utilise le paramètre callbackcomme rappel de blocage. Helper.Methodest appelée avec Loglaquelle agit comme une fonction de rappel. Lors de l'exécution, le message suivant est écrit sur la console : « Le rappel était : Hello world ».
classe publique MainClass { static void Main ( string [] args ) { Helper helper = new Helper (); helper . Method ( Log ); } static void Log ( string str ) { Console . WriteLine ( $"Le rappel était : {str}" ); } } classe publique Helper { public void Method ( Action < string > callback ) { callback ( "Bonjour tout le monde" ); } }
Kotlin
Dans le code Kotlin suivant , la fonction askAndAnswerutilise le paramètre getAnswercomme rappel de blocage. askAndAnswerest appelée avec getAnswerToMostImportantQuestionqui agit comme une fonction de rappel. L'exécution de cette fonction indiquera à l'utilisateur que la réponse à sa question est « 42 ».
fun main () { print ( "Entrez la question la plus importante : " ) val question = readLine () askAndAnswer ( question , :: getAnswerToMostImportantQuestion ) } amusant getAnswerToMostImportantQuestion (): Int { return 42 } amusant askAndAnswer ( question : String? , getAnswer : () -> Int ) { println ( "Question : $ question " ) println ( "Réponse : ${ getAnswer () } " ) }
JavaScript
Dans le code JavaScript suivant , la fonction calculateutilise le paramètre operatecomme rappel de blocage. calculateest appelée avec multiplypuis avec sumqui agissent comme des fonctions de rappel.
fonction calculer ( a , b , opérer ) { retourner opérer ( a , b ); } fonction multiplier ( a , b ) { retourner a * b ; } fonction somme ( a , b ) { retourner a + b ; } // génère 20 alerte ( calculer ( 10 , 2 , multiplier )); // génère 12 alerte ( calculer ( 10 , 2 , somme ));
La méthode de collection .each()de la bibliothèque jQuery utilise la fonction qui lui est transmise comme rappel de blocage. Elle appelle le rappel pour chaque élément de la collection. Par exemple :
$ ( "li" ). each ( function ( index ) { console . log ( index + ": " + $ ( this ). text ()); });
Les rappels différés sont couramment utilisés pour gérer les événements provenant de l'utilisateur, du client et des temporisateurs. Des exemples peuvent être trouvés dans addEventListener, Ajax et XMLHttpRequest.
En plus de l'utilisation de rappels dans le code source JavaScript, les fonctions C qui prennent une fonction sont prises en charge via js-ctypes.
Rouge et REBOL
Le code REBOL / Red suivant illustre l'utilisation du rappel.
- Comme l'alerte nécessite une chaîne, le formulaire produit une chaîne à partir du résultat du calcul
- Les valeurs get-word! (c'est-à-dire :calc-product et :calc-sum) déclenchent l'interpréteur pour renvoyer le code de la fonction plutôt que d'évaluer avec la fonction.
- Les références de type de données dans un bloc [float! entier!] restreignent le type de valeurs passées en arguments.
Rouge [ Titre : « Exemple de rappel » ] calculer : func [ num1 [ nombre! ] num2 [ nombre! ] fonction-de-rappel [ fonction! ] ][ fonction-de-rappel num1 num2 ] calc-produit : func [ num1 [ nombre! ] num2 [ nombre! ] ][ num1 * num2 ] calc-sum: func [ num1 [ nombre! ] num2 [ nombre! ] ][ num1 + num2 ] ; alertes 75, le produit de 5 et 15 formulaire d'alerte calcule 5 15 :calc-product ; alertes 20, la somme de 5 et 15 formulaire d'alerte calculer 5 15 :calc-sum
Lua
Un exemple d'interpolation de couleurs utilisant le moteur Roblox qui prend un rappel facultatif .done :
attendre ( 1 ) DT local = attendre () function tween_color ( objet , couleur_finition , temps_fondu ) local step_r = couleur_finition . r - objet . BackgroundColor3 . r local step_g = couleur_finition . g - objet . BackgroundColor3 . g local step_b = couleur_finition . b - objet . BackgroundColor3 . b local total_steps = 1 / ( DT * ( 1 / temps_fondu )) local terminé ; coroutine.wrap ( function () pour i = 0 , 1 , DT * ( 1 / fade_time ) do object . BackgroundColor3 = Color3 . new ( object . BackgroundColor3 . r + ( step_r / total_steps ), object . BackgroundColor3 . g + ( step_g / total_steps ), object . BackgroundColor3 . b + ( step_b / total_steps ) ) wait () fin si terminé alors terminé () fin fin )() return { done = function ( callback ) ended = callback fin } fin tween_color ( some_object , Color3.new ( 1 , 0 , 0 ), 1 ) .done ( function () print " Interpolation des couleurs terminée ! " end )
Python
Dans le code Python suivant , la fonction calculateaccepte un paramètre operatequi est utilisé comme rappel de blocage. calculateest appelée avec squarelaquelle agit comme une fonction de rappel.
def square ( val ): return val ** 2 def calculate ( operate , val ): return operate ( val ) # sorties: 25 calculate ( square , 5 )
Julia
Dans le code Julia suivant , la fonction calculateaccepte un paramètre operatequi est utilisé comme rappel de blocage. calculateest appelée avec squarelaquelle agit comme une fonction de rappel.
julia> square ( val ) = val ^ 2 square (fonction générique avec 1 méthode) julia> calculate ( operate , val ) = operate ( val ) calculate (fonction générique avec 1 méthode) julia> calculate ( square , 5 ) 25