![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() ![]() |
|||||
![]() |
|||||||
![]() |
![]() |
![]() |
![]() |
![]() |
|||
![]() |
![]() |
![]() |
![]() |
||||
![]() |
CHAPITRE
|
APPRENDRE
À PROGRAMMER AVEC FUTUREBASIC
|
NOTES
|
Les branchements III |
Leçon 12 : Paramètres des
fonctions Vous avez peut-être remarqué la similitude entre les deux instructions suivantes : Utilisateur = FN bienvenue$(Utilisateur) prenom = UCASE$(prenom) La fonction BASIC UCASE$ utilisée dans notre exemple accepte une chaîne de caractères en paramètre, passe les caractères en majuscule, puis retourne la chaîne résultante. C'est ici l'occasion de rappeler que les mots-clés du BASIC ne sont en fait que des fonctions-utilisateur enfouies dans le Runtime. Le Runtime étant lui-même écrit avec FutureBASIC, vous pouvez, si vous aimez le risque, modifier le code à votre guise puisque le code source des différents runtimes est accessible dans le dossier Headers du Compilateur. Si vous suivez pas à pas le travail accompli par l'ordinateur dans notre exemple précédent, vous réaliserez assez vite le "gaspillage" de ressources induit : L'ordinateur est peut-être infatigable, mais c'est beaucoup de travail pour un si petit résultat. Fort heureusement, il y a aussi des méthodes plus directes pour modifier une variable qui est passée en paramètre à une fonction. Si l'on s'attaque aux octets originaux qui composent la chaîne de départ, le travail sera plus immédiat, il consommera moins de mémoire et sera de surcroît plus rapide. Le bloc qui contient les octets est localisé à l'adresse de la variable. Lorsqu'on travaille avec les adresses, différentes stratégies peuvent être mises en œuvre. Pour commencer on peut utiliser indifféremment les deux méthodes suivantes pour le passage des paramètres: 1 - Passage de l'adresse de la variable à la fonction invoquée : LOCAL FN maFonction(adresse AS PTR) END FN FN maFonction (@variable) 2 - Réception de l'adresse par la fonction invoquée : LOCAL FN maFonction(@adresse AS PTR) END FN FN maFonction (variable) Une fois que vous disposez de l'adresse dans la fonction locale, il y a de très nombreuses façons d'examiner les octets et de les modifier. Vous pouvez utiliser la fonction PEEK BYTE (raccourci : valeur = |adresse|) pour lire et POKE BYTE (raccourci : | adresse,valeur) pour écrire un octet individuel. Rappelez vous que le premier octet trouvé à l'adresse de la chaîne nous renseigne sur le nombre de caractères qui la composent et que les autres octets représentent les codes ASCII des caractères. LOCAL FN Majuscule(@adresse AS PTR) DIM longueur AS SHORT DIM i AS SHORT DIM codeCaractere AS CHAR longueur = |adresse| LONG IF longueur FOR i = 1 TO longueur codeCaractere = |adresse + i| LONG IF codeCaractere >= _"a" AND codeCaractere <= _"z" | adresse + i, codeCaractere - 32 END IF NEXT END IF END FN // Programme principal DIM utilisateur AS STR63 Utilisateur = "Élodie" FN Majuscule(Utilisateur) PRINT "Bonjour ";utilisateur Que fait exactement notre fonction Majuscule ici ? Elle commence par intercepter l'adresse de la chaîne qui lui a été envoyée en paramètre et déclare les variables dont elle va avoir besoin. Elle extrait ensuite le premier octet trouvé à l'adresse de la chaîne pour ranger sa valeur dans la variable longueur qui contiendra donc le nombre de caractères composant la chaîne. Avant d'entamer une boucle pour examiner les caractères un à un, elle s'assure que la longueur est différente de zéro (que la chaîne passée n'est pas vide), car rappelez-vous que les boucles FOR/NEXT sont exécutées au moins une fois. Si la chaîne passée est vide, le programme n'entrera pas dans la boucle. Ensuite, à tour de rôle les caractères sont extraits individuellement et examinés. Si le code ASCII du caractère figure dans une certaine plage de valeurs (ici entre les codes du a minuscule et du z minuscule inclus), la fonction range à l'adresse du caractère, le code précédemment trouvé moins la valeur 32 (vous pouvez consulter la table des codes ASCII pour vérifier qu'il y a bien un écart de 32 entre les versions majuscule et minuscule des caractères courants). Ce code, bien qu'en apparence plus compliqué, est exécuté beaucoup plus rapidement que celui qui a été montré jusqu'ici. Il consomme moins de ressources également puisqu'aucune copie de chaîne n'est effectuée. Ce n'est pas la seule méthode, et vous aurez l'occasion de constater que les programmeurs adoptent différentes stratégies pour arriver au même résultat. Par exemple, essayez de comprendre le code suivant : LOCAL FN Majuscule(@adresse AS PTR) DIM adresseFinChaine AS LONG IF adresse.nil` = 0 THEN EXIT FN adresseFinChaine = adresse + adresse.nil` DO adresse++ LONG IF adresse.nil` >= _"a" AND adresse.nil` <= _"z" adresse.nil` -= 32 END IF UNTIL adresse = adresseFinChaine END FN // Programme principal DIM utilisateur AS STR63 Utilisateur = "Élodie" FN Majuscule(Utilisateur) PRINT "Bonjour ";utilisateur Désorienté ? Rassurez-vous, c'est normal, je ne vous ai pas encore indiqué tous les raccourcis d'écriture, mais notez que le bout de code ci-dessus, essentiellement identique au précédent, n'utilise qu'une seule variable servant à stocker l'adresse où la chaîne prend fin en mémoire. Pour vous aider à mieux comprendre, sachez que FutureBASIC^3 emprunte au langage C, certaines de ses tournures syntaxiques, par exemple : variable++ est équivalent à variable = variable + 1 ou encore INC(variable) variable-- est équivalent à variable = variable - 1 ou encore DEC(variable) variable += x est équivalent à variable = variable + x variable -= x est équivalent à variable = variable - x J'ai introduit ici également un raccourci que certains programmeurs FutureBASIC préfèrent aux traditionnels PEEK et POKE, consistant à indiquer un décalage par rapport à une adresse, ce décalage étant suivi d'un suffixe identificateur de type. Enfin, je dois ajouter que la constante _nil est fréquemment utilisée par les programmeurs en lieu et place de la valeur 0. Si bien que adresse.nil` désigne la valeur de l'octet (` étant l'idendificateur de type pour les octets), trouvé à un décalage de 0 octet à partir de adresse, ce qui revient à la forme d'écriture plus courante PEEK BYTE (adresse) ou PEEK (adresse) [BYTE étant optionnel lorsqu'on souhaite manipuler des octets] ou |adresse|. Notez que si l'on avait voulu prendre un entier long localisé 2 octets plus loin que l'adresse mémoire on aurait pu écrire ceci : adresse.2& qui est équivalent à PEEK LONG(adresse + 2) ou [adresse + 2]. En résumé, l'écriture "adresse+ point+décalage+symbole identificateur de type" est une forme alternative aux commandes PEEK et POKE, pour lire et écrire directement en mémoire. À ce point, vous avez peut-être le vague souvenir qu'il est possible de considérer une chaîne de caractères comme un tableau d'octets. Eh oui, la commande XREF pourrait être mise à profit ici aussi : LOCAL FN Majuscule(@adresse AS PTR) DIM i AS SHORT XREF cars(63) AS CHAR cars& = adresse i = 0 WHILE cars(i) i++ LONG IF cars(i) >= _"a" AND cars(i) <= _"z" cars(i) -= 32 END IF IF i = cars(0) THEN EXIT WHILE WEND END FN // Programme principal DIM utilisateur AS STR63 Utilisateur = "Élodie" FN Majuscule(Utilisateur) PRINT "Bonjour ";utilisateur Essayez de comprendre la logique du bout de code ci-dessus. Si vous rencontrez des difficultés, consultez à nouveau les chapitres consacrés à la commande XREF et à la structure conditionnelle WHILE/WEND. Enfin, parmi toutes les autres possibilités offertes par le langage, en voici une, qui semble fonctionner correctement et pour laquelle vous ne trouverez aucun exemple, ni mention dans les manuels : LOCAL FN Majuscule(cars(63) AS CHAR) DIM i AS SHORT LONG IF cars(0) FOR i = 1 TO cars(0) LONG IF cars(i) >= _"a" AND cars(i) <= _"z" cars(i) -= 32 END IF NEXT END IF END FN // Programme principal DIM utilisateur AS STR63 Utilisateur = "Élodie" FN Majuscule(Utilisateur) PRINT "Bonjour ";utilisateur Notez que cela revient à se passer totalement de la commande XREF. Cette forme ne fonctionne qu'avec des pointeurs sur des blocs non relogeables en mémoire. Chaque problème posé peut-être abordé sous un angle différent, il n'y a jamais une façon unique de résoudre un problème, et c'est à la charge du programmeur, en fonction des outils dont il dispose et parfois de son inspiration, de rechercher la solution la plus appropriée à un problème donné. Le fait qu'une fonction locale a la possibilité de modifier directement les variables qui lui sont passées en paramètres en travaillant directement avec leur adresse en mémoire, permet d'offrir une plus grande autonomie à la fonction par rapport au reste du programme. Encore une fois, l'un des intérêts majeurs de cette indépendance est de permettre l'écriture de fonctions complètement réutilisables dans d'autres projets sans plus de modifications. Un problème peut subsister cependant, si vous déclarez une variable dans votre fonction locale qui porte le même nom qu'une variable globale de votre projet. Rappelez-vous que les variables globales sont censées être "vues" par les fonctions locales, elles peuvent donc être examinées et modifiées à l'intérieur de ces fonctions. Pour isoler totalement votre fonction du reste du programme, FutureBASIC dispose d'une déclaration spéciale que l'on place avant la définition de la fonction : LOCAL MODE LOCAL FN maFonction // corps de la fonction END FN La commande LOCAL MODE empêche la fonction de connaître les variables globales du programme. Si par hasard, vous déclariez dans une fonction en LOCAL MODE une variable locale ayant le même nom qu'une des variables globales du programme, le Compilateur saura qu'il s'agit de deux variables différentes. On peut alors copier et coller de telles fonctions d'un projet à un autre sans grands risques d'engendrer d'éventuels conflits. Seules les constantes et les définitions de records déclarées à l'extérieur de la fonction lui resteront encore accessibles. Si l'on souhaite qu'une variable globale soit altérée par la fonction isolée par l'instruction LOCAL MODE, on pourra alors utiliser une des deux méthodes précédemment mentionnées : en faisant en sorte d'écrire la fonction pour qu'elle retourne une valeur que l'on rangera dans la variable globale à modifier ou bien en travaillant à partir de l'adresse-mémoire de la variable globale que l'on passera en paramètre. Bien qu'il soit un peu plus délicat, à peine plus en fait, de concevoir les fonctions de cette façon, il est certain que vous ne regretterez pas d'avoir pris cette bonne habitude lorsque vous en serez à votre quatrième ou cinquième projet. [Precédent] [Table des Matières] [Suivant] |
UCASE$ Il subsiste cependant un problèmes avec les routines de la Toolbox du Mac et les ligatures ß,? et ? qui n'ont pas d'équivalents en capitales, la routine retourne, incorrectement, les mêmes caractères et non pas 'SS', 'FI' et 'FL' |
![]() |
||
![]() |
||
FutureBASIC est une marque déposée appartenant à Staz Software, Inc et utilisée avec permission. |