Les variables, les opérateurs et les expressions
Dans Algae, les variables et les opérateurs se combinent pour former des expressions et des instructions. Les règles à cet effet sont pour la plupart conventionnelles; par exemple, l'instruction a=b+c signifie que la somme de b et c est affectée à la variable a. Dans Algae, cependant, le concept d'addition (ainsi que de nombreuses autres opérations) est étendu pour inclure des opérations entre diverses classes de données.
- Entités
- Variables
- Opérateurs
- Expressions
- Instructions
- Fonctions
- Entités
L'unité de base des données dans Algae est l'entité. Chaque entité possède un attribut appelé sa classe la décrivant comme un «scalaire», un «vecteur», une «matrice»,... La fonction intégrée class renvoie la classe de son paramètre sous forme de chaîne de caractères. Ainsi :
- x = 1.2E3; class(x)?
affiche "scalaire" et :
- class (cos)?
affiche "fonction". Naturellement, l'énoncé :
- class (class (log))?
affiche "scalaire", car la classe renvoie une chaîne de caractères scalaire.
Les entités contiennent également des membres, dans lesquels divers attributs supplémentaires de l'entité sont entreposés. Par exemple, les entités de matrice ont des membres tels que le type, la densité et la symétrie. Ces membres sont eux-mêmes des entités, il n'est donc pas inhabituel d'avoir plusieurs niveaux d'appartenance (un membre d'un membre d'un membre).
L'opérateur "point" est utilisé pour faire référence aux membres. Les instructions :
- x = 1; x.type?
affiche "integer", la valeur du membre de type dans x. La ligne :
- foo = 1492; foo.bar?
affiche "NULL", car les scalaires ne contiennent pas de membre bar lorsqu'ils sont créés. Vous pouvez bien sûr lui en donner un :
- foo.bar = "Columbus"
Variables
Une variable est un symbole faisant référence à une entité nommée ; l'entité est connue sous le nom de valeur du symbole. Une variable naît lorsqu'une valeur lui est attribuée ; il n'y a pas de déclaration explicite.
- Noms des variables
- Évaluation des variables
- Portée des variables
- Variables prédéfinies
Noms des variables
Les noms des variables sont composés de lettres, de chiffres, de signes dollar ($) et de traits de soulignement (_). Un nom de variable ne peut pas commencer par un chiffre. Les noms de variables sont sensibles à la casse, donc les variables foo et Foo sont distinctes.
On utilise généralement des noms commençant par $ pour faire référence aux variables globales étant définies au démarrage ou ayant un effet secondaire. Par exemple, la variable $beep agit comme n'importe quelle autre variable, mais contrôle également les effets sonores d'Algae. La fonction who ne signale pas les variables commençant par $ à moins que vous ne lui donniez le paramètre "$".
Un nom de variable est spécial : la variable $$ fait référence à la table des symboles globaux. Par exemple :
- a = "x";
- $$.(a)
affiche la valeur de la variable globale x. Vous pouvez utiliser $$ comme n'importe quelle autre variable, sauf que vous ne pouvez pas réaffecter sa valeur.
La seule limitation de la longueur d'un nom de variable est qu'il doit contenir au moins un caractère. Les noms courts sont pratiques pour le travail interactif, mais les noms plus longs et descriptifs présentent des avantages dans les fonctions et les scripts.
Évaluation des variables
Lorsqu'un nom de variable est rencontré dans une expression, il est évalué en remplaçant le nom de la variable par l'entité nommée correspondante. S'il s'agit d'un paramètre d'une fonction, une copie est alors effectuée (conceptuellement, sinon en fait) et passée à la fonction. Ainsi, dans un appel de fonction comme func(a), la fonction func n'obtient qu'une copie de a et ne peut pas modifier le a appartenant à son appelant.
Il existe un cas exceptionnel, impliquant l'opérateur "point", dans lequel un identifiant n'est pas évalué comme un nom de variable. Un identifiant du côté droit d'une expression membre est pris littéralement comme le nom du membre. Par exemple, l'instruction :
- v.type
affiche la valeur du membre de v appelé type. Vous pourriez très bien avoir une variable nommée type, mais cela n'affecterait pas l'expression du membre ci-dessus.
Alternativement, l'instruction :
- v.(type)
possède une expression à droite de l'opérateur «point», pas un simple identifiant, et affiche donc la valeur du membre de v nommé par le type de variable.
Portée des variables
Les variables référencées à partir de fonctions ont une portée globale, sauf si elles sont des arguments de la fonction ou sont déclarées locales avec l'instruction locale. Par exemple, la fonction :
- init = function ()
- {
- i = sqrt (-1);
- e = exp (1);
- pi = acos (-1);
- };
pourrait être utilisé pour initialiser certaines variables couramment utilisées. Une fois que vous avez exécuté init(), les variables i, e et pi seraient définies globalement.
En même temps, vous pourriez avoir une autre fonction :
- egg_hunt = function (v)
- {
- local (i);
- for (i in 1:v.ne)
- {
- if (v[i] == 0) { return i; }
- }
- }
dans laquelle i a une portée locale. Ici, vous ne pouvez pas modifier ni même obtenir la valeur de la variable globale i.
La forme de l'instruction locale est similaire à un appel de fonction, et plusieurs variables peuvent être spécifiées en les séparant par des points-virgules.
L'instruction locale est inhabituelle, dans la mesure où il s'agit d'une directive à l'analyseur plutôt que de quelque chose de codé. Cela signifie qu'elle prend effet lorsque l'analyseur la voit, et non lorsqu'elle est exécutée. Dans le code :
- f = function ()
- {
- a = 1;
- if (0) { local (a); }
- a = 2;
- }
la variable globale a est définie sur 1. Même si le code dans le bloc if n'est pas exécuté, après l'avoir vu, l'analyseur considère a comme une variable locale. Normalement, on place simplement les instructions locales au début d'une fonction.
L'instruction veil vous permet d'effectuer une modification temporaire d'une variable globale restant active à partir de ce point jusqu'à la fin de la portée dynamique actuelle, généralement jusqu'à la fin de la fonction. Par exemple, la fonction :
- prt = function (x)
- {
- veil (pi);
- pi = "apple";
- bake ();
- };
modifie la valeur de la variable globale pi pendant son exécution, mais pi revient à sa valeur précédente lorsque prt se termine. Si la fonction bake faisait référence à pi, elle utiliserait ou modifierait la valeur temporaire.
Par portée dynamique, nous faisons référence à l'unité d'exécution actuelle. Dans une fonction, cela dure aussi longtemps que la fonction s'exécute. Sinon, la portée dynamique est le fichier actuel, sauf que pour les fonctions eval et exec, il s'agit de la chaîne de caractères de paramètre elle-même. De plus, si l'exécution est en mode interactif et non dans une fonction, la portée dynamique s'étend uniquement jusqu'à la fin de la dernière instruction d'une ligne.
Contrairement à l'instruction locale, l'effet de l'instruction veil a lieu au moment de son exécution. Une fois qu'une variable est voilée (et avant de la définir sur autre chose), elle a la même valeur que la variable globale associée.
Variables prédéfinies
Plusieurs variables sont déjà définies pour vous au démarrage d'Algae. Certaines d'entre elles servent à contrôler Algae, et d'autres vous fournissent des informations. Les variables prédéfinies sont :
Variable prédéfinie | Description |
---|---|
$beep | Si cette variable est «true» (c'est-à-dire que test($beep) renvoie 1), les messages d'erreur incluent un caractère BEL. Cela provoque l'émission de bips sur la plupart des terminaux. Au démarrage, $beep est égal à 0. |
$digits | Cette variable spécifie le nombre de chiffres significatifs qu'Algae affiche pour les nombres réels et complexes. Au démarrage, $digits est égal à 4. |
$pid | Au démarrage, $pid contient l'«identifiant de processus» du processus Algae. |
$program | Au démarrage, $program donne le nom du programme Algae en cours. Ce sera normalement "algae". Ce serait différent si, par exemple, vous aviez changé le nom de l'exécutable Algae. |
$prompt | Cette variable contrôle le prompt interactive d'Algae. Il s'agit d'un vecteur de caractères avec deux éléments : au démarrage, il s'agit de : ( "> ", " " ) Le premier élément est l'invite de premier niveau d'Algae. Lorsqu'Algae attend une autre ligne d'entrée avant d'exécuter ce qu'elle a déjà vu, elle présente son prompt de second niveau. Le second élément de $prompt définit ce prompt de second niveau. Si Algae ne peut pas comprendre la valeur de $prompt, elle se passe de prompt. |
$read | La variable $read est définie comme un effet secondaire de la fonction readnum. Elle indique le nombre de valeurs lues lors du dernier appel à readnum. |
$term_width | Cette variable indique à Algae la largeur de votre terminal en caractères. Algae va essayer d'ajuster son affichage pour s'adapter à cette largeur. Au démarrage, Algae va essayer de détecter cela par lui-même. Si term_width est défini sur 0, Algae ne bouclera pas les lignes. |
help | Il s'agit simplement d'un scalaire de caractère fournissant quelques instructions de base pour l'utilisation d'Algae. |
Vous souhaiterez peut-être définir certaines de ces variables (comme $beep ou $prompt) à chaque fois que vous utilisez Algae. Pour ce faire, placez les affectations dans le fichier «.algae» de votre répertoire personnel.
Opérateurs
Algae dispose d'opérateurs unaires, binaires et ternaires pour effectuer diverses opérations. Les opérateurs arithmétiques standards (`+', `-', `*', `/',...) sont disponibles et ont la priorité habituelle. Le signe circonflexe `^' sert à l'exponentiation : 8^2 est égal à 64. Le caractère guillemet simple `'' désigne la transposition conjuguée.
À quelques exceptions près, tous les opérateurs fonctionnent dans le sens «élément par élément». Par exemple, si A et B sont des matrices de même taille, alors A/B renvoie une matrice dont chaque élément est le quotient des éléments correspondants de A et B. L'opérateur `*' fonctionne dans le sens du «produit scalaire», donc A*B est une multiplication de matrice. L'opérateur `@' est l'opérateur de multiplication «élément par élément» d'Algae.
Lorsqu'un opérateur possède des opérandes de tableau de types différents, l'un d'eux sera automatiquement converti afin qu'ils aient un type commun, si possible. Un entier peut être converti en réel ou en complexe, et un réel peut être converti en complexe. Aucune conversion vers ou depuis le type caractère n'est possible.
Contrairement à d'autres langages, le résultat d'une opération dans Algae peut avoir un type différent de ses opérandes. Par exemple, le résultat de 1/2 est de type réel, même si ses opérandes sont de type entier. Un autre exemple est (-1)^0.5, renvoyant un nombre complexe.
À l'exception de *, si un opérateur binaire a à la fois un vecteur et une matrice comme opérandes, le vecteur est converti en matrice avant que l'opération ne soit effectuée. Lors de la conversion, un vecteur devient une matrice avec une seule ligne. Par exemple, dans l'instruction :
- ( 1, 2 ) + [ 3, 4 ]
l'opérande de gauche de + est le vecteur (1,2). Celui-ci est converti en matrice avant d'être ajouté à [3,4].
Le tableau suivant résume la priorité et l'associativité des opérateurs d'Algae. Ceux affichés sur la même ligne ont la même priorité, et les lignes sont classées par ordre de priorité décroissante.
Opérateurs | Associativité |
---|---|
() [] . | de gauche à droite |
' | de droite à gauche |
^ | de droite à gauche |
! + - | (unaire) de gauche à droite |
* / % @ | de gauche à droite |
+ - | (binaire) de gauche à droite |
< > <= >= == != | de gauche à droite |
& | de gauche à droite |
| | de gauche à droite |
&& | de gauche à droite |
|| | de gauche à droite |
: | de gauche à droite |
, | de gauche à droite |
= += -= *= /= @= %= | de droite à gauche Appels de fonction `()' Références d'éléments `[]' Références de membres `.' Transposer `'' Puissance `^' Pas `!' Négation `+' et `-' (unaire) Multiplication `*', `/', `%' et `@' Addition `+' et `-' (binaire) Relation `<', `>', `<=', `>=', `==' et `!=' Et `&' Ou `|' Court et `&&' Court ou `||' Générer `:' Ajouter `,' Affecter `=', `+=', `-=', `*=', `/=', `@=' et `%=' |
Appels de fonctions
Les fonctions sont appelées de la manière habituelle : la référence de fonction est suivie d'une liste d'arguments. Par exemple :
- w = union (u; v);
appelle la fonction union, en lui passant les valeurs de u et v pour les arguments. La valeur renvoyée par l'instruction return de la fonction (ou NULL si elle n'en a pas) est la valeur de l'appel de fonction.
Les paramètres peuvent être n'importe quelles expressions et sont séparés par des points-virgules. La fonction appelée obtient uniquement les valeurs des expressions que vous lui donnez comme paramètres, elle ne peut donc pas modifier les variables que vous lui donnez.
À partir de sa définition, la fonction sait combien d'arguments attendre. Passer trop de paramètres est une erreur. Si vous passez moins de paramètres que ce que la définition de fonction appelle, des valeurs NULL sont passées à la place des paramètres manquants.
Outre leur utilisation dans les appels de fonction, les parenthèses sont utilisées à plusieurs autres fins, notamment les expressions de regroupement, les conditions if, for et while, les définitions de fonction,...
Références d'éléments
L'opération de référence d'élément renvoie des éléments vectoriels ou des lignes et colonnes de matrice spécifiés. Appliquée à une table, l'opération est effectuée sur chacun des membres de la table.
Les éléments sont référencés en suivant une expression vectorielle ou matricielle avec une liste entre parenthèses des éléments souhaités. Pour le vecteur v, l'expression v[w] donne les éléments spécifiés par w. L'expression entre parenthèses doit être un scalaire ou un vecteur. S'il s'agit d'une matrice, elle sera convertie en vecteur si possible. Ainsi, par exemple :
- v[3:8]
affiche les troisième à huitième éléments de v. Si le vecteur entre parenthèses a un type numérique, il est converti en entier si nécessaire (en prenant la partie réelle et en arrondissant) puis utilisé pour faire référence aux numéros d'éléments.
La classe de l'expression de référence d'élément est déterminée par la classe des spécificateurs. Une fois que tous les spécificateurs de matrice sont convertis en vecteurs, la dimension du résultat est égale à la somme des dimensions des spécificateurs. Par exemple, M[1;2] est un scalaire, M[[1];2] et M[1;[2]] sont des vecteurs et M[[1];[2]] est une matrice. Notez que la classe du résultat ne dépend pas de la classe de l'entité d'origine.
Si le vecteur entre parenthèses a un type caractère, il est alors considéré comme faisant référence aux étiquettes d'éléments plutôt qu'aux numéros d'éléments. Par exemple, si vous définissez x comme :
- x = 1:3;
- x.eid = "this", "that", "other";
alors x["that"] renvoie 2, la valeur de l'élément de x ayant l'étiquette "that". Si les étiquettes n'ont pas de type caractère, elles sont temporairement converties en type caractère pour la comparaison.
Les références d'éléments fonctionnent de la même manière pour les matrices, sauf que les lignes et les colonnes sont spécifiées. Par exemple :
- M[ 1,3; 7:12 ]
spécifie les lignes 1 et 3, colonnes 7 à 12, de la matrice M.
Les spécificateurs ne doivent pas nécessairement être inutiles. Par exemple, M[1,1,1;] renvoie trois copies de la première ligne de M.
Outre leur utilisation pour les références d'éléments, les parenthèses sont également utilisées pour former des matrices.
Références de membres
Les membres d'une entité sont référencés avec l'opérateur "point". Par exemple, x.type renvoie la valeur du type de membre de x. Notez que, si l'opérande de droite est un identifiant, il est alors considéré littéralement comme le nom du membre souhaité. Vous pouvez avoir une variable appelée type, mais cela n'a aucune importance lorsqu'il s'agit d'évaluer x.type.
D'un autre côté, si l'opérande de droite est une expression, sa valeur (convertie en un scalaire de caractères) est considérée comme le nom du membre souhaité. Comme vous pouvez transformer un identifiant en expression simplement en l'enfermant entre parenthèses, l'expression x.(type) utilise la valeur du type de variable comme nom du membre.
Les noms de membres ne partagent pas les mêmes limitations que les noms de variables. En fait, n'importe quelle chaîne de caractères ASCII (à l'exception de NUL) fonctionnera. Cela peut être très utile. Par exemple, vous pouvez configurer un «vecteur» d'entités (de n'importe quelle classe) comme dans :
- V = {};
- V.(1) = A;
- V.(2) = B;
- V.(3) = C;
- V.(4) = D;
et ensuite faire référence aux «éléments» individuels (c'est-à-dire les membres de V) par numéro. Vous pouvez gérer plusieurs dimensions en faisant référence à l'élément «3,2,4», par exemple.
Transposer
L'opérateur de transposition applique l'opération de transposition conjuguée à une matrice. Pour les types entier, réel et caractère, cela signifie déplacer chaque élément de la ligne i et de la colonne j vers la ligne j et la colonne i. Si la matrice est de type complexe, l'opération de conjugaison complexe est également appliquée.
Si vous voulez la transposition d'une matrice complexe M, et non sa transposition conjuguée, utilisez l'expression conj(M').
Si la transposition est appliquée à un scalaire ou à un vecteur, l'entité est d'abord convertie en matrice puis transposée. Par exemple, (1,2)' est identique à [1;2]---le vecteur est d'abord converti en une matrice à une ligne puis transposé pour former une matrice à une colonne.
Si cet opérateur est appliqué à une table, l'opération est effectuée sur chaque membre de cette table.
Puissance
L'opérateur «puissance» «^» est un opérateur binaire élevant son opérande gauche à la puissance de son opérande droit. Ainsi, 2^3 donne 8. Il s'associe de droite à gauche, donc les expressions x^y^z et x^(y^z) sont équivalentes.
Lorsque des vecteurs et des matrices sont impliqués, l'opérateur «puissance» fonctionne dans un sens «élément par élément». Par exemple, si M est une matrice, alors M^2 met au carré chaque élément de M. Ce n'est certainement pas la même chose que M*M !
Dans une expression telle que 2^M, où l'opérande gauche est scalaire et l'opérande droit est un vecteur ou une matrice, le résultat a chaque élément i de M remplacé par 2^M[i]. Par exemple :
- 2^(0:3)
affiche le vecteur (1,2,4,8).
Remarquez que la priorité de «^» est supérieure à «-», donc l'expression -1^2 renvoie -1.
En termes mathématiques, 0^0 n'est pas défini --il génère une erreur dans Algae.
Si les opérandes gauche et droit sont des tableaux, ils doivent avoir des dimensions et des étiquettes correspondantes.
Si cet opérateur est appliqué à une table, l'opération est alors effectuée sur chaque membre de cette table.
Pas
L'opérateur «!» est un opérateur préfixe unaire renvoyant 1 si son opérande est considéré comme «faux» et 0 sinon. Les opérandes «faux» sont :
- NULL.
- Entités numériques dans lesquelles chaque élément est nul.
- Entités de caractères dans lesquelles chaque élément est « ».
- Vecteurs et matrices sans éléments.
- Tableaux sans éléments.
Si cet opérateur est appliqué à une table, l'opération est alors effectuée sur chaque membre de cette table.
Négation
L'opérateur de négation unaire «-» multiplie son argument numérique par -1. L'opérateur «+» est là pour la commodité de l'utilisateur -- Algae l'ignore dans son contexte unaire. Par exemple, +-1 donne un résultat négatif. D'un autre côté, 1+-1 et 1-+1 renvoient tous deux 0.
Si l'un de ces opérateurs est appliqué à une table, cette opération est alors effectuée sur chaque membre de la table.
Multiplication
Les opérateurs «*» et «@» effectuent tous deux une multiplication. La différence est que «@» effectue une multiplication élément par élément, tandis que «*» effectue une multiplication par produit scalaire. Les opérandes de «@» doivent avoir des dimensions et des étiquettes correspondantes ; chaque élément de son opérande gauche est multiplié par l'élément correspondant de l'opérande droit. Par exemple :
- ( 1, 2, 3 ) @ ( 4, 5, 6 )
renvoie le vecteur (4,10,18).
Lorsque `*' est appliqué aux matrices, le nombre de colonnes de l'opérande gauche doit être égal au nombre de lignes de l'opérande droit, et les étiquettes correspondantes doivent correspondre. Le résultat est une matrice qui a le même nombre de lignes que l'opérande gauche et le même nombre de colonnes que l'opérande droit.
Si l'un des opérandes de `*' est un vecteur et l'autre une matrice, le vecteur est (conceptuellement) converti en matrice avant que la multiplication ne soit effectuée. S'il est à gauche, il est converti en une matrice avec une ligne ; s'il est à droite, il est converti en une matrice avec une colonne.
La multiplication de deux vecteurs donne leur produit scalaire.
L'opérateur `/' effectue une division. Comme `@', il s'effectue dans un sens élément par élément.
Si l'un des opérandes de l'un de ces opérateurs est un scalaire, alors l'opération est effectuée dans un sens élément par élément. Par exemple :
- 2*(1,2,3)
donne (2,4,6) --- chaque élément du vecteur est multiplié par le scalaire.
L'opérateur `%' effectue l'opération de module, produisant le reste lorsque l'opérande gauche est divisé par l'opérande droit. Par exemple, (2,2.5,3)%2 renvoie le vecteur (0,0.5,1).
Le résultat a le même signe que l'opérande gauche. Si cet opérateur est appliqué à une table, l'opération est alors effectuée sur chaque membre de la table.
Addition
Les opérateurs «d'addition» sont les opérateurs binaires `+' et `-'. Si leurs opérandes sont numériques, ils effectuent alors les opérations normales d'addition et de soustraction.
Lorsque l'opérateur `+' est appliqué à des chaînes de caractères, il les enchaîne. L'opérateur `-' n'est pas défini pour les chaînes de caractères.
Entre deux tables, `+' combine leurs membres dans la table résultante. Conceptuellement, les membres de l'opérande gauche sont insérés en premier dans cette nouvelle table, suivis des membres de l'opérande droit. Cela signifie que si les opérandes ont un membre avec le même nom, la valeur de ce membre dans la table résultante provient de l'opérande droit. Par exemple, si t est une table qui contient le membre "foo", alors le résultat de :
- t + { foo = NULL }
est identique à t sauf que son membre «foo» a la valeur NULL.
Lorsque l'opérateur «-» est appliqué aux tables, le résultat est une table qui contient tous les membres de l'opérande gauche à l'exception de ceux étant également dans l'opérande droit. Par exemple :
- {x;y;z}-{x;y}
renvoie une table contenant le membre unique « z ».
Si un seul des opérandes est une table, l'opération est alors effectuée entre l'autre opérande et chaque membre de la table.
Relation
Les opérateurs de relation `<', `>', `<=', `>=', `==' et `!=' renvoient «true» (1) ou «false» (0) pour refléter la vérité de l'expression. Par exemple :
- (1:5) < 3
renvoie (1,1,0,0,0).
Si les deux opérandes sont des tableaux, leurs dimensions et leurs libellés doivent correspondre.
Contrairement à la plupart des opérateurs, `==' et `!=' autorisent NULL comme opérande. Dans ce cas, l'autre opérande est simplement vérifié pour voir s'il s'agit ou non d'un NULL.
Si l'un de ces opérateurs est appliqué à une table, cette opération est alors effectuée sur chaque membre de la table.
Et
L'opérateur `&' effectue l'opération logique "et" élément par élément. Par exemple :
- x = 1:5;
- x > 2 & x < 4
affiche (0,0,1,0,0).
Si cet opérateur est appliqué à une table, l'opération est alors effectuée sur chaque membre de cette table.
Ou
L'opérateur `|' effectue l'opération logique « ou » élément par élément. Par exemple :
- x = 1:5;
- x < 2 | x > 4
affiche (1,0,0,0,1).
Si cet opérateur est appliqué à une table, l'opération est alors effectuée sur chaque membre de cette table.
Et court-circuit
L'opérateur `&&' effectue l'opération logique "et" de "court-circuit". Les deux opérandes sont évalués pour la "vérité" comme s'ils étaient le test d'une instruction if. Si l'opérande de gauche est "false", alors l'opérande de droite n'est pas évalué. Si les deux opérandes sont "true", le résultat de l'opération est 1 ; sinon le résultat est 0. Notez que cette opération est assez différente de celle de l'opérateur `&', fonctionnant élément par élément.
L'exemple de code suivant provient de la fonction solve :
- if (options != NULL && members (options) == "pos")
- {
- A = chol (A);
- else
- A = factor (A);
- }
La variable options est d'abord vérifiée pour voir si elle est NULL. C'est seulement si elle n'est pas NULL que la fonction membre est appelée. C'est exactement ce que nous voulons, car ce serait une erreur d'appeler member avec un paramètre NULL.
Ou court
L'opérateur `||' effectue l'opération logique "ou" de "court-circuit". Les deux opérandes sont évalués pour la "vérité" comme s'ils étaient le test d'une instruction if. Si l'opérande de gauche est "vrai", alors l'opérande de droite n'est pas évalué. Si l'un des opérandes est "vrai", le résultat de l'opération est 1 ; sinon le résultat est 0. Notez que cette opération est assez différente de celle de l'opérateur `|', fonctionnant élément par élément.
Dans l'instruction :
- if (options == NULL || options.tol == NULL) { tol = 1e-6; }
la variable options est d'abord vérifiée pour voir si elle est NULL. C'est seulement si elle n'est pas NULL que la référence de membre options.tol est évaluée.
Générer
Les vecteurs numériques peuvent être générés avec l'opérateur `:'. Une expression comme i:j génère un vecteur commençant par la valeur de i. Si j est supérieur à i, chaque élément successif est supérieur de 1 au précédent, le dernier élément étant inférieur ou égal à j. Si j est inférieur à i, chaque élément successif est inférieur de 1 au précédent, le dernier élément étant supérieur ou égal à j.
L'opérateur `:' a également une forme ternaire, comme dans i:j:k. Cela fait la même chose que i:j sauf que les éléments successifs diffèrent de k au lieu de 1.
Pour les opérandes complexes, l'opération peut être décrite conceptuellement dans le plan complexe. Une ligne est tracée entre les points i et j. Le vecteur résultant contient alors les points situés sur ce segment de ligne commençant à i, se poursuivant vers j, espacés d'une distance k (ou 1, si k n'est pas donné).
Ajouter
L'opérateur `,' "ajoute" ses opérandes. Si les opérandes sont des vecteurs, le résultat est un nouveau vecteur contenant les deux opérandes. Si les opérandes sont des matrices, le résultat est une nouvelle matrice contenant l'opérande gauche à gauche et l'opérande droit à droite.
Si cet opérateur est appliqué à une table, l'opération est alors effectuée sur chaque membre de cette table.
Affecter
Les opérateurs d'affectation sont `=', `+=', `-=', `*=', `/=', `@=' et `%='. L'opérateur `=' renvoie la valeur de l'opérande droit, définissant l'opérande gauche sur cette valeur dans le processus. Par exemple :
- a = b = c = 1;
attribue 1 aux variables a, b et c. Les opérateurs d'affectation associent de droite à gauche, donc c reçoit d'abord la valeur 1. Le résultat de cette expression, c = 1, est 1, donc cette valeur est utilisée avec b comme si elle était b = 1.
Remarquez qu'un test comme :
- if (i = j) ...
est complètement différent de :
- if (i == j) ...
Dans le premier exemple, la valeur de j est attribuée à i, puis cette valeur est testée par l'instruction if. Dans le deuxième exemple, l'instruction if teste l'égalité de i et j. Ainsi, si i est 1 et j est 2, le premier exemple teste vrai (et modifie la valeur de i) et le deuxième teste faux.
Les éléments individuels d'un tableau peuvent être modifiés ; par exemple :
- x[3;4:6] = 0, 0, 0;
définit à 0 les éléments de x dans la ligne 3 et les colonnes 4 à 6. L'opérande de droite est converti au même type que l'opérande de gauche, si possible, et les dimensions doivent correspondre. Cependant, si l'opérande de droite est un scalaire, il est rempli à la taille appropriée. Ainsi, l'exemple précédent pourrait tout aussi bien s'écrire :
- x[3;4:6] = 0;
Une dernière chose à propos des affectations de tableaux : si la variable de gauche est un vecteur, mais que vous spécifiez deux dimensions pour elle, alors cette variable sera convertie en matrice. De même, si la variable de gauche est une matrice, mais que vous ne spécifiez qu'une seule dimension, elle sera convertie en vecteur. Par exemple, dans le code :
- A = B = [ 1, 2, 3 ];
- A[2] = B[;2] = 9;
A et B commencent tous deux comme des matrices. Le deuxième élément de chaque tableau est ensuite modifié en 9. Au final, cependant, A est transformé en vecteur car une seule dimension lui a été donnée. B reste une matrice.
Les autres opérateurs d'affectation (`+=', `-=',...) sont simplement là pour des raisons pratiques. L'expression i+=j signifie la même chose que i=i+j, et i-=j signifie la même chose que i=i-j. Si le côté gauche est une expression, vous devez garder à l'esprit qu'elle sera évaluée deux fois. Par exemple :
- x[ func() ] += 1
effectuera en fait deux appels à la fonction func.
Expressions
Les affectations sont effectuées de manière normale : a=5 définit la variable « a » pour contenir le scalaire 5. Les variables ne sont pas déclarées avant utilisation, mais prennent plutôt la structure et le type de ce qui leur est attribué. Vous pouvez, par exemple, saisir :
- a=1;
- a=[a,a];
dans ce cas, la variable «a» est d'abord un scalaire puis une matrice. Une liste des variables précédemment définies (à l'exception des fonctions) est donnée par la fonction «who()», et une variable peut être supprimée en lui attribuant NULL.
En plus des variables simples, des éléments et des membres peuvent également être spécifiés sur le côté gauche d'une affectation. Par exemple, l'instruction :
- A[ 1:4; 1:4 ] = 3;
attribue 3 à tous les éléments de la première partition 4x4 de la matrice «A», laissant les autres éléments de A inchangés. Des valeurs peuvent être attribuées aux membres de la même manière. Par exemple, les instructions :
- A = [ 1,2,3; 4,5,6 ];
- time = [ 1.1, 2.2, 3.3 ];
- A.rid = [ "first"; "second" ]
- A.cid = time
assigner les libellés de ligne et de colonne de «A».
Instructions
Les instructions se terminent par un point d'interrogation, un point-virgule ou une nouvelle ligne. L'utilisation d'un point d'interrogation ou d'une nouvelle ligne entraîne l'impression de la valeur de l'instruction ; avec un point-virgule, l'impression est supprimée. Une nouvelle ligne termine une instruction uniquement si elle n'apparaît pas entre parenthèses, crochets ou accolades.
La fin d'une instruction est implicite par l'accolade fermante d'une instruction «if», «for» ou «while» ou d'une expression «function». L'impression est activée lorsque le terminateur est implicite. Par exemple :
- for (i in 1:10) { i }
affiche tous les entiers de 1 à 10.
Lorsque des nombres réels ou complexes sont affichés, Algae affiche 4 chiffres significatifs par défaut. Cela peut être modifié avec la fonction digits, ou simplement en définissant la variable globale $digits.
Les commentaires sont introduits avec le caractère «#». Ce caractère et tous ceux le suivant sur la même ligne sont ignorés (à l'exception du saut de ligne).
Fonctions
Les fonctions sont définies par une expression de fonction, se composant du mot clef «function», suivi d'une liste de paramètres entre parenthèses, suivie d'un ensemble d'instructions entourées d'accolades. Par exemple, la fonction max d'Algae est définie par quelque chose comme :
- max = function (x) { return x[imax(x)]; };
Les fonctions ne sont qu'une autre classe d'entités ; l'instruction ci-dessus définit une fonction puis l'affecte à la variable max.
Les paramètres sont des variables locales à la fonction. À l'entrée, ils prennent les valeurs des arguments formels dans l'expression appelante. Passer plus de paramètres que ce qui est prévu dans la définition de la fonction est une erreur. Si moins de paramètres sont passés, des valeurs NULL sont passées à la place des paramètres manquants.
La référence de fonction n'a pas besoin d'être un identifiant : une expression de fonction fonctionne parfaitement. Par exemple :
- child = (his_functions + her_functions).reproduce();
combinerait les tables his_functions et her_functions, référencerait le membre reproduce, l'appellerait sans paramètres, puis affecterait le résultat à child. Notez que, puisque `.' et `()' ont la même priorité, leur associativité de gauche à droite fait que la référence au membre se produit en premier, puis l'appel de fonction. Dans une expression comme sin(1).type, l'appel de fonction est effectué en premier.
Lorsqu'une fonction est compilée, des informations sont incluses reliant ses opérations au nom de fichier et aux numéros de ligne du code source. Si une erreur se produit, ces informations sont utiles pour en rechercher la cause. Dans certains cas, ces informations sont indésirables et peuvent être supprimées avec la fonction strip. La fonction message est un tel cas.