Précédent
Accueil

Génèse informatique

3 ième partie

En décrivant ce qu'est une fonction, on décrit un premier genre de variable locale que sont les variables muettes de la fonction.

1) Le constructeur de fonction, `"|"`

A titre d'exemple, considérons un langage `a,b,s("."), f(".,."), g(".,.,."),k("...")`

On définit une fonction `"|"` appelé constructeur de fonction. Il prend en premier argument une séquence de variables muettes distinctes et en second argument un programme écrit dans le langage augmenté des variables muettes, qui peuvent venir masquer des variables de même noms déjà existantes dans le contexte parent. Et on le munie d'une syntaxe centrée de priorité plus faible que la virgule, pour en faire un opérateur. Par exemple :

`x,y "|" f(f(a,y),x)`

L'opérateur `"|"` appliqué à la séquence de variables muettes `x,y,z` et au terme ` f(f(a,y),x)` produit la fonction correspondante. Autrement dit ce terme est un programme, que l'on peut appliquer directement à 2 arguments `(u,v)` :

`(x,y"|"f(f(a,y),x))(u,v) ↬  f(f(a,v),u)`

Vous remarquerez que le premier membre définit 2 nouvelle variables libres `x,y` dont la portée s'étend sur le corps de la fonction. Il y a donc un contexte propre au corps de la fontion qui contient un langage enrichie de ces 2 variables supplémentaires. La fonction `"|"` prend comme premier argument une séquence composée de variables, et elle n'évalue pas ces variables, elle prend seulement leur nom qu'elle rajoute au contexte comme nouvelles variables, masquant les variables de même noms déjà référencées dans le contexte, pour former un nouveau contexte dont la portée s'arrête au second membre appelé le corps de la fonction. Notez que la fonction ainsi produite peut s'appliquer à un terme contenant des variables de même noms que celles utilisé dans l'appel, sans que cela n'ait d'importance, car les variables d'appel sont muette, ce sont de nouvelles variables libres.

La fonction ainsi définie est d'arité fixe, égale à 2. Cela a pour conséquence que appliquée à une séquence de taille différente, la fonction retourne `sf"nada"`. On peut proposer un autre comportement, en spécifiant des valeurs par défauts des derniers arguments. Exemple :

`x, y "=" s(a) "|" f(f(a,y),x)`

L'opérateur d'égalité est de priorité syntaxique plus forte que celle de la virgule. La fonction ainsi construite est d'arité 1 ou 2. Elle s'applique nécessairement à un premier membre désigné par la variable muette `x` car ce membre n'a pas de valeur par défaut. Le membres suivant dans l'appel peut être omis car il a une valeur par défaut.

Etant donné une variable `z`. Notez que la fonction nullaire `() "|" z` n'est pas identique au terme `z`. En effet, l'évaluation de `z()` va exécuter le programme contenue dans la variable `z` en ne prenant aucun argument, tandis que `(() "|" z)()` est égale à `z`. Si la variable `z` est libre, l'évaluation de `z()` donnera le terme `z()` et non le terme `z`.

2) Le constructeur de fonction, `AA`, `"↦"`

Nous avons une notation plus explicite, où figure la déclaration des variables muettes :

`AAx, x "↦" f(x,x)`

Et où le meta-opérateur `"↦"` est un simple opérateur de construction de couple, construisant une arrête appartenant au graphe de la fonction que l'on définit. La méta-opération est transférée à l'opérateur de déclaration de variable universelle `AA` qui prend en argument une liste de nouvelles variables dont la portée s'étend au corps de la fonction, c'est à dire au bloc où se trouve cette déclaration. Ainsi ce terme désigne un ensemble d'arrêtes définissant le graphe d'une fonction, et ainsi définie une fonction sans lui donner de nom. On peut alors appliquer cette fonction à l'entrée `u` :

`(AAx, x"↦"f(x,x)) (u) "↦" f(u,u)`

3) Unification entre l'appel et la tête de la fonction

On perfectionne la définition de fonction. Le premier membre de l'opérateur de construction de fonction, devient un terme quelconque appelé la tête de la fonction, devant contenir toutes les variables déclarées universellement, tandis que le second membre est appelé le corps de la fonction. Cela restreint le domaine de définition aux seuls termes qui s'unifient avec la tête de la fonction. Exemple :

`AA(x,y), (x,f(x,x),f(a, y))"↦"g(x,y,b)`

Cela définit une fonction qui appliquée à `b,f(b,b),f(a,s(a))` retourne `g(b,s(a),b)`. En revanche appliqué à `a,f(a,b),f(a,a)`, elle retourne `sf"nada"` qui indique que l'on applique la fonction en dehors de son domaine de définition.

4) Fonction par morceau

On définit une fonction par morceau par une séquence de fonctions. Exemple de fonction définie en trois morceaux :

`AA(x,y)`
    `(s(x),y)|->f(x,y)`
    `(x,s(y))|->g(x,y,x)`
    `(x,y)|->g(x,x,y)`

Le passage à la ligne correspond à un opérateur de priorité syntaxique plus faible. Par défaut, la fonction va utiliser la première sous-fonction qui marche c'est à dire qui aboutit à un résultat autre que `sf"nada"`. On peut également par le même procédé définir des fonctions ayant plusieurs arités. Exemple :

`AA(x,y)`
    `s(x)|->f(x,x)`
    `(s(x),y)|->f(x,y)`
    `(x,y,a)|->f(y,x)`

Cette fonction est suceptible de prendre 1 ou 2 ou 3 arguments.

`AA(x,"*"u,"*"v)`
`(x,x)|->f(x,y)`
`(x,h("*"u),"*"v)|->f(g(x,"*"v),"*"u)`
`(x,"*"u)|->"*"u`

Cette fonction est suceptible de prendre 2 ou plus d'arguments.

Les listes de fonctions ainsi présentées correspondent à des disjonctions. On exige que l'unification réussisse pour au moins un appel de fonction. Il n'est pas utile de développer l'autre versant qu'est la conjonction car unififer deux fois sur deux termes distincts correspond à une seul unification sur l'intersection qui est obtenue en unifiant les des deux termes en question.

5) Ensemble générique

On définit un ensemble générique par une entête déclarant de nouvelle variables suivie par un terme. Exemple :

`E = ( AA(x,y), alpha(x,alpha(x,y)) )`

On définit un ensemble générique par morceau, par une entête déclarant de nouvelle variables suivie par une séquence de termes. Exemple :

`E = ( AA(x,y), alpha(x,alpha(x,y)), y(x), alpha(x) )`

Les ensembles génériques par morceau sont implémentés pareillement que les fonctions unaires par morceau. Elle correspondent à des fonctions d'arité de sortie nulle qui appliqué à un terme ne retourne que l'éventuelle exception, vrai ou faux, selon que l'unification à réussie ou non. Et elle constitue la fonction caractéristique de l'ensemble.

6) Déclaration de variable

L'instruction `sf"var" x` déclare une nouvelle variable de nom `x` initalement libre. Elle est ajoutée au langage en cours. Et s'il existe déjà une variable de même nom, alors celle-ci est masquée par la nouvelle variable. La variable qui vient juste d'être masquée par `x` se note `"↑"x` et celle-ci peut masquer une autre variable de même nom qui se note alors `"↑↑"x` et ainsi du suite.

Néanmoins on ne crée pas un contexte après chacune de ses instructions. On définie un contexte seulement pour chaque bloc de code comprenant de telles instructions indépendament du lieu où elles sont placés dans le bloc de code.

Les définitions de fonction et d'ensemble générique vu précédement sont des blocs de code avec déclaration de variable. L'instruction `AAx,` se comporte comme l'instruction `sf"var" x` en activant en plus un mécanisme fonctionnel définissant les fonctions par morceau et les ensembles génériques.

L'utilisation d'une variable `y` non déclarée dans le bloc va hériter du bloc parent (héritage statique) et s'il n'y a pas de déclaration elle est alors considérée comme étant déclarée au plus haut niveau, définissant ainsi une variable globale.

On s'inspire d'un travail précédent https://vulgate.toile-libre.org/Informatique/Interpreteur.htm

7) Bloc de code

Un sous-programme est un bloc de code. C'est un objet plus générale qu'une fonction mais que l'on désigne souvent comme une fonction par abus de langage. Il est constituer d'une liste d'instructions entre cochet `{...}`. Le bloc de code, comme la fonction, peut être désigné par une variable que l'on considère alors comme son nom. Exemple

`A={...}`

Les variables déclarées localement dans le bloc de code n'ont de portée que dans le bloc de code. On relie le bloc de code au reste du programme par une liste de variables d'entrée et une liste de variable de sortie, qui constitue un proptotypage. Et ce prototypage peut mettre en oeuvre une liaison nominale par defaut qui peut être faible ou attributive ou forte telle que la notation du physicien pour écrire un système d'équations avec un système de coordonnée par défaut.

Le bloc de code est également relié au reste du programme par une liste de variables non localement déclarées qui héritent soit du bloc parent (héritage statique) ou soit du bloc appelant (héritage dynamique).

8) Héritage statique et dynamique

Les variables non déclarées localement qui apparaissent dans le corps du bloc de code sont liées par héritages statiques ou dynamique selon leur genre. Il y a donc deux genres de variable non-local, les variables statiques et les variables dynamiques. Mais pour détecter le genre on commence par faire jouer l'héritage statique pour rechercher l'éventuelle déclaration `sf"vardyn"` de la variable.

Dans un bloc `A`, une variable non déclarée si elle est statique, hérite du bloc parent contenant `A` , tandis qu'une variable dynamique hérite du bloc contenant l'appel de `A`.

9) La liaison nominale forte

Le bloc de code comprend une tête qui nomme les entrées et une queue qui nomme des sorties. Et le corps du bloc du code comprend les instructions. Exemple :

`A={x,y,z "→" ... "→" u,v}`

Les variables `x,y,z,u,v` sont d'une portée plus grande qui couvre le bloc parents contenant le bloc `A` ou le bloc appellant `A` pour les variables dynamiques.

Le bloc `A` établit une liaison forte, déterminant les variable `u,v` en fonction des variable `x,y,z`. Il comprend un flag indiquant s'il est à jour où pas. Si l'une de ces trois variables `x,y,z` change, alors le flag est mis à faux, et l'appel d'une des variables `u` ou `v` va relancer leur calcul et remettre le flag à vrai. Cette liaison nominale forte qui a pour but de mettre en oeuvre la notation du physicien avec des systèmes de coordonnées par défaut, ce note sous forme d'un neurone :

`(u,v) ← (x,y,z)`

Délors la variable `u` possède un système de coordonnée par défaut `(x,y,z)` de tel sorte que `u=u(x,y,z)`, et `v=v(x,y,z)`. Ainsi la simple lecture de `u` ou de `v` peut enclencher le calcul si la valeur à besoin d'être mise à jour.

10) La liaison nominale attributive

Le bloc de code se comporte comme un objet ayant comme attribue ces variables d'entrée/ sortie. Le bloc de code comprend une tête qui nomme les entrées et une queue qui nomme des sorties. Et le corps du bloc du code comprend les instructions. Exemple :

`A={x,y,z "༎" ... "༎" u,v}`

Les variables `x,y,z,u,v` sont des attributs du bloc `A` et perdure après l'exécution de celui-ci. Ainsi `A"."x` accède à la variable `x` du bloc de code `A`. On définit une méthode `sf"ejecutar"` pour exécuter le code du bloc. Ainsi l'appel se présente comme suit :

`">>"A"."x`
`">>"A"."y`
`">>"A"."z`
`A"."sf"ejecutar"`
`A"."u">>"`
`A"."v">>"`

L'appel nominale se fait dans le contexte de la fonction, et les arguments sont traités en paralèle, ainsi :

`A(y"="b,z"="c,x"="a) "==" A(a,b,c)`
`A(x"+"1,z,y) "==" A(a"+"1,c,b)`
`A(x"-"1,x,x) "==" A(a,a"+"1,a"+"1)`
`A(y"="b) "==" A(a,b,a"+"1)`

Les variables locales définie par `sf"att"` constituent des atttributs du bloc, et peuvent donc jouer le rôle d'entrée sortie.

11) La liaison nominale faible

Le bloc de code comprend une tête qui nomme les entrées et une queue qui nomme des sorties. Et le corps du bloc du code comprend les instructions. Exemple :

`A={x,y,z "|" ... "|" u,v}`

Les variables `x,y,z` sont locales au bloc `A` et permettent l'appel nominal. Exemple :

A(y=b,z=c,x=a) == A(a,b,c)

Les variables `u,v` sont locales au résultat et se comportent comme des attributs d'un objet résultat. Exemple :

`r"="A(a,b,c)`
`r.u">>"`
`r.v">>"`

12) Naissance du concept d'objet et de classe d'objets

Le bloc de code contient certaines variables appelées attributs qui perdurent après son exécution. Se pose alors la question de leur intialisation. Elles se fait lors de la première exécution du bloc de code. Le bloc de code représente alors un objet dont une méthode implicite correspond à l'execution du bloc de code.

 

 

 

 

 

 

 

 

 

 

---- 2 juillet 2025 ----

 

 

 


Dominique Mabboux-Stromberg

 

Précédent
Accueil