Les fonctions et les procédures ================================ Préliminaire : rappel de théorie de cybernétique ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Théorie de l'information (Claude Shannon, 1949), (ou théorie de la communication) Canal de transmission:: entrée -> récepteur -> émetteur -> sortie .. glossary:: cybernétique étude des fonctions de réflexes conditionnés du cerveau humain utilisation au mieux en PNL ("programmation neuro-linguistique") ou en analyse transactionnelle, ou au pire en ingérinerie sociale. La matérialité physique est considérée comme le hardware, le génétique (le réseau neuronal) étant assimilé au network hardware. Les objets mentaux (fonctionnements psychologiques et épigénétiques du cerveaux) est assimilé au logiciel, au software. IFTTT ("if this then that") : la causalité mondaine est ramenée à un ordre de comportement affecté à un assimilé-machine. L'humain est ramené à une machine. C'est articulation entre "déclencheur contextuel" et "action en réponse de" n'est pas une "black box" mais un "feedback" qui, pour l'humain, loin d'être ramené à une entrée/sortie, constitue un **feedback** utile pour la connaissance de soi. A la place, la communication est ramenée à une **boucle de rétroaction** (comme dans un prompt) entre un système comportemental et son environnement. La représentation sujet/objet (la perspective traditionnelle) est remplacée par le clivage intérieur/extérieur. Behaviorisme, procédural. L'humain est donc ramené à - un ordonnanceur - un comportement intrinsèque (boîte noire) - un stimuli pavlovien (déclencheur, trigger) est considéré comme un paramètre d'entrée - une instruction comportementale est considérée comme une action de traitement - le résultat est observé. Cette articulation entre "déclencheur contextuel" et "action en réponse" est très exactement une forclusion de la profondeur monadique (Leibniz) de l'humain à la black box informationnelle (et cybernétique). Pour quoi faire ? Pour pirater. Pour manipuler. Le piratage consiste à - isoler les constantes (les procédures répétitives, les algorithmes) - les observer (collecter les données) afin de - les réécrire (influence toxique, pishing - hammeçonnage) - les détruire (attaque en règle) Description d'une procédure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ En programmation impérative, un programme est une suite d’instructions qui font évoluer l’état mémoire, le résultat est dans l’état final de la mémoire. Une procédure n'est rien d'autre qu'une fonction dont le résultat est de type unit - une procédure peut prendre des paramètres - elle modifie l'état courant du système - Déclaration des paramètes - Déclaration du corps - Appel de la procédure .. raw:: latex \begin{algorithm} \caption{Procédure de permutation de deux entiers}\label{permutation} \begin{algorithmic}[1] \Procedure{permuter}{$a,b$}{} \BState \emph{parametres}: \State $a: \textit{int}$ \State $b: \textit{int}$ \BState \emph{locales}: \State $z: \textit{int}$ \Comment{Une variable intermédiaire est nécessaire} \BState \emph{corps}: \State $z \gets a$ \State $a \gets b$ \State $b \gets z$ \EndProcedure \State \Call{permuter}{10, 12} \Comment{appel de la procédure} \end{algorithmic} \end{algorithm} effet de bord toute modification de la mémoire ou modification d'un support externe instruction commande ou phrase en mesure de modifier l'état du programme ou de la machine hôte (allocation mémoire, support externe, disque, écran...) Une procédure ne renvoie pas de valeur, mais provoque un 'effet de bord' (écriture dans une globale, dans un flux sortant etc.). Une procédure permet de créer une instruction nouvelle qui deviendra une primitive pour le programmeur. Cela permet de structurer le texte source du programme et améliorer sa lisibilité. Cela permet aussi d'appeler plusieurs fois, et à plusieurs endroit dans le code, cette primitive. Appel d'une procédure ~~~~~~~~~~~~~~~~~~~~~ (ex: pseudo-pascal) **déclaration de procédure** .. raw:: latex \begin{algorithm} \caption{Procédure de permutation de deux entiers}\label{appelpermutation} \begin{algorithmic}[1] \Procedure{permuter}{$a,b$}{} \BState \emph{parametres}: \State $a: \textit{int}$ \Comment{paramètres formels de la procédure} \State $b: \textit{int}$ \BState \emph{locales}: \State $z: \textit{int}$ \Comment{les variables locales de la procédure} \BState \emph{corps}: \State ... \Comment{Le corps de la procedure} \EndProcedure \State \Call{permuter}{10, 12} \Comment{l'appel de la procédure} \end{algorithmic} \end{algorithm} - les variables x1,...,xn sont appelées *paramètres formels* de p - les variables v1,...,vm sont appelées *les variables locales* de p les valeurs effectivement passées en paramètres, ici `10, 12` sont appelées **paramètres effectifs** de p signature C'est l'ensemble paramètre formel + resultat de l'appel fermeture L'ensemble procédure + variables locales + signature + parametres effectifs est appelé une **fermeture**. C'est la procédure + son contexte qui permet de l'instancier dans un programme. Environnement Contexte d’évaluation d'une expression ou d'une fonction Portée La portée d'un identifiant (une variable) est sa condition d'utilisation dans un contexte donné (utilisation locale uniquement, ou bien globale, ou bien locale et globale) La portée d’une liaison est la portion du code dans laquelle cette liaison est valide (i.e. où un identifiant est lié à une expression). .. ifconfig:: exercice **Exercice** : Que donne ce code ? .. code-block:: ocaml # let x = 42 in let y = x - 1 in x - y ;; .. ifconfig:: correction **Correction** : .. code-block:: ocaml - : int = 1 .. code-block:: ocaml let a = 3 (* première liaison pour l'identifiant a *) let b = 5 and c = 6 let somme = a + b + c val somme : int = 14 let a = 45 (* deuxième liaison pour l'identifiant a *) somme val a : int = 45 .. ifconfig:: exercice **Exercice** : Que donne ce code ? .. code-block:: ocaml let a = 3 and b = 4 and c = 8 ;; let somme = a + b + c ;; val somme : int = ??? let a = 44 let b = 5 let c = 1 somme - : int = ??? .. ifconfig:: correction .. code-block:: ocaml let a = 3 and b = 4 and c = 8 ;; - : int = 15 let somme = a + b + c ;; val somme : int = 15 let a = 44 let b = 5 let c = 1 somme - : int = 15 Même code en python .. code-block:: python >>> a = 1 >>> b = 2 >>> c = 3 >>> somme = a + b + c >>> somme 6 >>> a = 56 >>> b = 5678 >>> c = 56789 >>> somme 6 >>> Portée locale dans une expression .. code-block:: ocaml # let a = 2 and b = 3 and c = 4 in let somme = a+b+c in somme - : int = 9 # somme ;; Error: Unbound value somme # a ;; Error: Unbound value a .. important:: L’ordre d’évaluation dans un let ... in ... est bien déterminé, sans grande importance dans un cadre purement fonctionnel, mais important en cas d’effets de bord Exemple de variable globale modifiée localement (**attention, mauvaise pratique** !) : .. code-block:: python >>> a = 5 >>> def print_a(): ... print("La variable a = {0}.".format(a)) ... >>> print_a() La variable a = 5. >>> a = 8 >>> print_a() La variable a = 8. >>> niveau Le niveau d’une déclaration (de variable ou de procédure) est le nombre de procédures sous lesquelles elle est déclarée. Le programme principal a le niveau 0. .. code-block:: python :linenos: def _get_config(name): # return config value if not isfile(CONFIG_FILE): raise Exception("Fichier de configuration non existant") from ConfigParser import ConfigParser cfg = ConfigParser(allow_no_value=True) cfg.read(CONFIG_FILE) if name == "SUBNETS": return eval(cfg.get('eole', 'subnets')) elif name == "LEASES_FILE": DHCP_PATH = cfg.get('eole', 'container_path_dhcp') return join('/', DHCP_PATH, 'var/lib/dhcp/dhcpd.leases') def get_routes(*args, **kwargs): """ Send list of reserved IP return list of tuple (id, machine name, IP, MAC Adress) """ cfg = creole_loader(load_extra=True, rw=False, owner=MODNAME, mandatory_permissive=False) return zip(cfg.dhcp.dhcp.id_dhcp.id_dhcp, cfg.dhcp.dhcp.id_dhcp.hostname, cfg.dhcp.dhcp.id_dhcp.ip, cfg.dhcp.dhcp.id_dhcp.macaddress) On voit que l'objet `cfg` ligne 6 et 7 a le même nom que l'objet `cfg` ligne 19. C'est autorisé et les espaces de nommages sont différents. Description d'une fonction ~~~~~~~~~~~~~~~~~~~~~~~~~~ Une fonction renvoie une valeur et ne modifie pas l'état courant du programme en cours d'exécution ni ne réalise d'effets de bord. Elle renvoie **toujours** quelque chose (même la valeur ``None`` qui n'est pas rien) - une procédure peut prendre des paramètres - elle modifie l'état courant du système - Déclaration des paramètes - Déclaration du corps - Appel de la fonction En programmation fonctionnelle, programme est un ensemble de définitions de fonctions, un résultat est l'application d’une fonction à une structure de données effective. Les fonctions en programmation fonctionelles vont en réalité beaucoup plus loin, justifiant le nom de programmation fonctionnelle. - elles sont des valeurs de première classe c'est-à-dire des valeurs pouvant être créées par des calculs, passées en argument à des fonctions ou retournées, comme n'importe quelles autres valeurs. - une fonction peut être une expression comme une autre, alors anonyme - une application partielle peut être définie (curryfication) - base : la fonction - opération de base : l’application .. raw:: latex \begin{algorithm} \caption{Exemple de fonction}\label{fonction} \begin{algorithmic}[1] \Function{permuter}{$a,b$}{} \Comment{définition de la fonction} \BState \emph{parametres}: \Comment{déclaration (noms, types) des paramètres formels} \State $a: \textit{int}$ \State $b: \textit{int}$ \BState \emph{locales}: \Comment{déclaration (noms, types) des valeurs locales} \State $z: \textit{int}$ \BState \emph{corps}: \State $z \gets a$ \State $a \gets b$ \State $b \gets z$ \BState \emph{return}: \Comment{La valeur, le résulat renvoyé par la fonction} \EndFunction \State \Call{permuter}{10, 12} \Comment{appel de la fonction} \BState \emph{result}: \State (12, 10) \Comment{Le résultat effectif de la fonction après appel} \end{algorithmic} \end{algorithm} .. ifconfig:: exercice **Exercice** : factoriser le code suivant :: Ecrire "Etes-vous marié ?" Rep1 <- "" TantQue Rep1 <> "Oui" et Rep1 <> "Non" Ecrire "Tapez Oui ou Non" Lire Rep1 FinTantQue ... Ecrire "Avez-vous des enfants ?" Rep2 <- "" TantQue Rep2 <> "Oui" et Rep2 <> "Non" Ecrire "Tapez Oui ou Non" Lire Rep2 FinTantQue .. ifconfig:: correction **Correction** : :: Fonction RepOuiNon() en caractère Truc <- "" TantQue Truc <> "Oui" et Truc <> "Non" Ecrire "Tapez Oui ou Non" Lire Truc FinTantQue Renvoyer Truc Fin Ecrire "Etes-vous marié ?" Rep1 <- RepOuiNon() ... Ecrire "Avez-vous des enfants ?" Rep2 <- RepOuiNon() Définition mathématique ~~~~~~~~~~~~~~~~~~~~~~~~~ fonction Une fonction f d’un ensemble E vers un ensemble F est une correspondance qui associe à chaque élément de E au plus un élément de F. - E est appelé le domaine de définition - F est appelé codomaine - la **signature** de la fonction : `E → F (int -> int = )` Exemple de signature d'une fonction :: Fonction RepOuiNon(Msg en Caractère) en Caractère Ecrire Msg Truc <- "" TantQue Truc <> "Oui" et Truc <> "Non" Ecrire "Tapez Oui ou Non" Lire Truc FinTantQue Renvoyer Truc Fin Fonction ... Rep1 <- RepOuiNon("Etes-vous marié ?") ... Rep2 <- RepOuiNon("Avez-vous des enfants ?") ... curryfication évaluation de l'application d'une fonction - évaluter `(f x y)` - peut donner une **valeur fonctionnelle** - évaluation de la valeur fonctionnelle sur une valeur des types de base :: let g = function n -> (function p -> p + 1) n;; Typage d'une fonction ~~~~~~~~~~~~~~~~~~~~~ .. code-block:: ocaml # let f x y z = if x > 0 then y + x else z - x;; val f : int -> int -> int -> int = c’est en fait une fonction à un argument qui retourne une fonction:: .. code-block:: ocaml val f : int -> (int -> (int -> int)) = application de f à trois valeurs .. code-block:: ocaml # f 1 2 3;; - : int = 3 en programmation fonctionnelle, les fonctions sont des valeurs comme les autres .. code-block:: ocaml # fun x -> x * x;; - : int -> int = Récursivité ~~~~~~~~~~~~ .. code-block:: ocaml let rec fact n = if n=0 then 1 else n * fact (n-1) équivalent impératif utilisant une boucle .. code-block:: c int fact(int n){ int f = 1 ; int i = n ; while (i>0){ f = f * i; i-- ; } ; return f ; } Définitions par cas ~~~~~~~~~~~~~~~~~~~ .. code-block:: ocaml let rec fact n = match n with 0 -> 1 | -> n * fact (n-1) **exemple** : la fonction puissance .. code-block:: ocaml let rec puissance x n = match n with 0 -> 1 | -> x * puissance x (n-1) .. ifconfig:: exercice **Portée locale dans une fonction** Quelles sera la valeur de la variable `a` ? .. code-block:: python >>> a = 1 >>> def myfunc(): ... a = 2 ... return a + 1 ... >>> a = myfunc() + a .. ifconfig:: correction Correction: .. code-block:: python >>> a = 1 >>> def myfunc(): ... a = 2 ... return a + 1 ... >>> a = myfunc() + a >>> a 4 >>> .. ifconfig:: exercice **Exercice** : Portée locale dans une fonction avec variable globale Quelles sera la valeur de la variable `a` ? .. code-block:: python >>> a = 1 >>> def myfunc(): ... global a ... a = 2 ... return a + 1 ... >>> a = myfunc() + 3 >>> .. ifconfig:: correction **Correction** : .. code-block:: python >>> a = 1 >>> def myfunc(): ... global a ... a = 2 ... return a + 1 ... >>> myfunc() 3 >>> a 2 >>> a = myfunc() + 3 >>> a 6 >>>