Voici les étapes non-exhaustives à effectuer lorsque l'AGL WINDEV® est récalcitrant :
1) Réparer le projet (mise à niveau)
2) Recompiler le projet
3) Fermer le projet
4) Supprimer les dossiers "<NomDuProjet.cpl>", "Historique" et "Sauvegarde" du projet
5) (optionnel) Supprimer le fichier <NomDuProjet.env>
6) Ouvrir à nouveau le projet
En complément des pages d'aide PCSOFT® sur la Gestion des erreurs en exécution .
Il existe 2 types d'erreurs en exécution :
- les erreurs non fatales et non bloquantes (exemple : la lecture du contenu d'un fichier externe qui n'existe pas)
- les erreurs fatales et bloquantes (exemple : la division d'un nombre par zéro)
La gestion des erreurs non fatales peut être effectuée dans le traitement avec les solutions suivantes :
SI PAS <condition> ALORS
// Code de traitement de l'erreur non fatale...
// Pour obtenir toutes les informations sur la dernière erreur non fatale :
Trace(ErreurInfo(errComplet))
SINON
// Suite du code...
FIN
SI ErreurDétectée() ALORS
// Code de traitement de l'erreur non fatale...
// Pour obtenir toutes les informations sur la dernière erreur non fatale :
Trace(ErreurInfo(errComplet))
SINON
// Suite du code...
FIN
ErreurChangeParamètre(...)
CAS ERREUR:
// Code de traitement de l'erreur non fatale...
// Pour obtenir toutes les informations sur la dernière erreur non fatale :
Trace(ErreurInfo(errComplet))
- En cliquant sur le lien "Si Erreur : par programme" dans l'entête d'un code
La gestion des erreurs fatales peut être effectuée dans le traitement avec les solutions suivantes :
QUAND EXCEPTION DANS
// Code pouvant provoquer une erreur fatale...
FAIRE
// Code de traitement de l'erreur fatale...
// Pour obtenir toutes les informations sur la dernière erreur fatale :
Trace(ExceptionInfo(errComplet))
FIN
// Suite du code
CAS EXCEPTION:
// Code de traitement de l'erreur fatale...
// Pour obtenir toutes les informations sur la dernière erreur fatale :
Trace(ExceptionInfo(errComplet))
- En cliquant sur le lien "Quand exception : par programme" dans l'entête d'un code
Pour traiter une erreur fatale propagée au niveau du code d'initialisation du projet :
QUAND EXCEPTION
sFichierException est une chaîne = ComplèteRep(fRepExe()) + DateVersChaîne(DateSys(),"AAMMJJ") +"_"+ "Exceptions.log"
// Écriture du dump de débogage
dbgSauveDumpDébogage(fExtraitChemin(sFichierException, fDisque+fFichier+fRépertoire) + ".wdump")
// Écriture de l'erreur fatale dans un fichier log
fSauveTexte(sFichierException, ...
"------------------------------------------------------------------" +RC+ ...
"------------------------------------------------------------------" +RC+ ...
DateVersChaîne(DateSys(),"JJ/MM/AAAA")+"-"+HeureVersChaîne(HeureSys(),"HH:MM:SS")+" : " +RC+ ...
"--------------------------------------------------------EXCEPTION-" +RC+ ...
ExceptionInfo(errComplet) +RC+ ... // Dernière erreur fatale
"-----------------------------------------------------------ERREUR-" +RC+ ...
ErreurInfo(errComplet) +RC+ ... // Dernière erreur non fatale
fChargeTexte(sFichierException) ...
)
FIN
En complément de la page d'aide PCSOFT® sur les variables de type réel .
rTemp1 est un réel
rTemp2 est un réel
// Utiliser la syntaxe 0n devant la valeur pour obtenir une précision maximale
rTemp1 = 0n1.234567
rTemp2 = 0n1.234568
// L'opérateur "=" est précis à 10e-6 près sur les réels
SI (rTemp1 <> rTemp2) ALORS
// La conversion des réels en chaîne utilise un algorithme complexe donc la trace est correcte
Trace( "NOK", rTemp1, rTemp2 )
FIN
rTemp1 = 0n1.2345678
rTemp2 = 0n1.2345679
SI (rTemp1 = rTemp2) ALORS
// La conversion des réels en chaîne utilise un algorithme complexe donc la trace est correcte
Trace( "OK", rTemp1, rTemp2 )
FIN
// (*) signifie que le format de la variable s'adapte automatiquement à la valeur contenue
xTemp1 est un numérique (*)
xTemp2 est un numérique (*)
// Utiliser 0n devant la valeur pour forcer l'affectation d'un type numérique
xTemp1 = 0n1.23456789012345678901234567890123456789
xTemp2 = 0n1.23456789012345778901234567890123456788
SI (xTemp1 <> xTemp2) ALORS
Trace( "NOK", xTemp1, xTemp2 )
FIN
Pour les calculs avancés, le type réel est plus rapide que le type monétaire. De même que le type monétaire est plus rapide que le type numérique.
En complément de l'aide sur les différents types d'entiers :
Type d'entiers | Min | Max |
---|---|---|
Entier sur 1 octet - TINYINT | -128 | 127 |
Entier sur 2 octets - SMALLINT | -32 768 | 32 767 |
Entier sur 4 octets (ou Entier) - INT |
-2 147 483 648 | 2 147 483 647 |
Entier sur 8 octets - BIGINT | -9 223 372 036 854 775 808 | 9 223 372 036 854 775 807 |
Entier sans signe sur 1 octet - TINYINT UNSIGNED |
0 | 255 |
Entier sans signe sur 2 octets - SMALLINT UNSIGNED |
0 | 65 535 |
Entier sans signe sur 4 octets (ou Entier sans signe) - INT UNSIGNED |
0 | 4 294 967 295 |
Entier sans signe sur 8 octets - BIGINT UNSIGNED |
0 | 18 446 744 073 709 551 615 |
Pour le type "Entier système" : si compilé en 32 bits voir "Entier sur 4 octets", si compilé en 64 bits voir "Entier sur 8 octets".
Les valeurs exactes de ces différents types d'entiers se trouvent dans le fichier "Limites.wl" présent dans le répertoire "...\Personnel\Externe\"
Si une variable numérique est manipulée comme un opérateur logique (booléen) alors le zéro est équivalent à Faux et toutes les autres valeurs sont équivalentes à Vrai.
Donc les deux lignes de code sont équivalentes :
SI nIndice ALORS ...
SI (nIndice <> 0) ALORS ...
PCSOFT® recommande d'utiliser la première syntaxe.
Pour une optimisation du temps d'exécution du code alors utiliser la première syntaxe sinon pour une meilleure compréhension du code, utiliser la seconde syntaxe.
PROCÉDURE PUBLIQUE GLOBALE HConnexion_HFSQL(cnxConnexion est une Connexion, LOCALE sHPasse est une chaîne, LOCALE nNumPort est un entier sans signe, LOCALE sServeur est une chaîne, LOCALE sBaseDeDonnees est une chaîne, LOCALE sUtilisateur est une chaîne, LOCALE sMotDePasse est une chaîne) <métier> : booléen
// En cas d'erreur, on sort en renvoyant Faux et on propage l'erreur
ErreurChangeParamètre(epRenvoyerErreur, Faux)
// Permet de modifier le délai de connexion
HCS.DélaiRéponse = INTRANET // Temps d'attente de réponse faible
// Permet de modifier la taille de la trame de données envoyée
// Dans le cas général, il n'est pas nécessaire d'intervenir sur la taille des trames
HCS.TailleTrame = (HCS.DélaiRéponse = INTERNET) ? hTrameInternet SINON hTrameDéfaut
//Niveau de sécurité/protection souhaité :
HSecurite(0)
// - 0 : mécanisme de sécurité désactivé. La vitesse d'écriture dans les fichiers de données est maximale.
// - 1 : mécanisme de sécurité activé. La vitesse d'écriture est plus lente qu'avec l'option 0, mais la sécurité est assurée lors de l'écriture dans les fichiers de données.
// - 2 : mécanisme de sécurité maximal. La vitesse d'écriture dans les fichiers de données est plus lente qu'avec l'option 1.
// Description de la connexion
cnxConnexion..Serveur = sServeur + ((nNumPort > 0) ? ":"+NumériqueVersChaîne(nNumPort) SINON "")
cnxConnexion..BaseDeDonnées = sBaseDeDonnees
cnxConnexion..Utilisateur = sUtilisateur
cnxConnexion..MotDePasse = sMotDePasse
cnxConnexion..Provider = hAccèsHFClientServeur
cnxConnexion..Accès = hOLectureEcriture
cnxConnexion..OptionsCurseur = hCurseurClient
// https://doc.pcsoft.fr/fr-FR/?9000124&name=informations_optionnelles_connexion
cnxConnexion..InfosEtendues = ""
cnxConnexion..Cryptage = hCryptageRC5_16
cnxConnexion..Compression = (HCS.DélaiRéponse = INTERNET)
// Optimiser les temps de connexion aux bases HFSQL Client/Serveur : https://doc.pcsoft.fr/fr-FR/?9000176
// Affectation de la connexion à tous les fichiers de données
HChangeConnexion("*", cnxConnexion)
// Fixe le mot de passe utilisé pour créer ou pour ouvrir les fichiers de données
SI PAS (sHPasse ~= "") ALORS
HPasse("*", sHPasse)
FIN
// Ouverture de la connexion
HOuvreConnexion(cnxConnexion)
// Désactive la gestion des triggers applicatifs
HGèreTrigger(Faux)
// Gestion des caches dans le moteur HFSQL Client/Serveur (1 à 5000)
// La valeur par défaut sur un PC est 1000.
HGèreCache(hTaillePage, 2000)
// Permet de modifier la priorité de l'application appelante.
// Cette valeur est comprise entre -10 (basse priorité) et 10 (haute priorité).
// Par défaut, la priorité est de 0 (priorité normale).
HPriorite(cnxConnexion, 5)
SI PAS (sBaseDeDonnees ~= "") ALORS
RENVOYER (Position(TAB+HListeBaseDeDonnées(cnxConnexion, hLstNormal)+TAB, TAB+sBaseDeDonnees+TAB) > 0)
SINON
RENVOYER Vrai
FIN
// https://doc.pcsoft.fr/fr-FR/?5515003
// https://doc.pcsoft.fr/fr-FR/?5515005&name=connecteur_natif_sql_server_specificites_remarques
PROCÉDURE PUBLIQUE GLOBALE HConnexion_MSSQL(cnxConnexion est une Connexion, LOCALE sServeur est une chaîne, LOCALE nNumPort est un entier sans signe, LOCALE sBaseDeDonnees est une chaîne, LOCALE sUtilisateur est une chaîne, LOCALE sMotDePasse est une chaîne) <métier> : booléen
// En cas d'erreur, on sort en renvoyant Faux et on propage l'erreur
ErreurChangeParamètre(epRenvoyerErreur, Faux)
// H.ModeSQLServer (Variable) : https://doc.pcsoft.fr/fr-FR/?3044276&name=hmodesqlserver_variable
// 0 pour forcer l'utilisation du Connecteur Natif SQL Server via OLE DB (wd280sqlserver.dll ou wd280sqlserver64.dll).
// 1 pour utiliser le Connecteur Natif installé. Si les deux versions du Connecteur Natif sont installées, le Connecteur Natif via OLE DB est utilisé en priorité.
// 3 pour forcer l'utilisation du Connecteur Natif SQL Server via ODBC (wd280sqlserver2.dll ou wd280sqlserver2_64.dll).
H.ModeSQLServer = 1
// Description de la connexion
cnxConnexion..Serveur = sServeur + ((nNumPort > 0) ? ","+NumériqueVersChaîne(nNumPort) SINON "")
cnxConnexion..BaseDeDonnées = sBaseDeDonnees
cnxConnexion..Utilisateur = sUtilisateur
cnxConnexion..MotDePasse = sMotDePasse
cnxConnexion..Provider = hAccèsNatifSQLServer
cnxConnexion..Accès = hOLectureEcriture
cnxConnexion..OptionsCurseur = hCurseurClient
// https://doc.pcsoft.fr/fr-FR/?9000124&name=informations_optionnelles_connexion
// WD Command Timeout : http://vincent-lecomte.blogspot.fr/2013/03/wd17-le-delai-dattente-de-la-requete.html
cnxConnexion..InfosEtendues = "Initial Catalog="+sServeur+"; WD Connection Timeout=10; WD Command Timeout=120"
// Ouverture de la connexion
HOuvreConnexion(cnxConnexion)
// Affectation de la connexion à tous les fichiers de données
HChangeConnexion("*", cnxConnexion)
// Désactive la gestion des triggers applicatifs
HGèreTrigger(Faux)
RENVOYER Vrai
PROCÉDURE PUBLIQUE GLOBALE HConnexion_Classic(cnxConnexion est une Connexion, LOCALE sHPasse est une chaîne, LOCALE sCheminBDD est une chaîne) <métier> : booléen
// En cas d'erreur, on sort en renvoyant Faux et on propage l'erreur
ErreurChangeParamètre(epRenvoyerErreur, Faux)
// Optimisation de l'accès aux données en HFSQL Classic.
HChangeLocalisation("*", hDisque)
// Description de la connexion
//VariableRAZ(cnxConnexion)
cnxConnexion..Serveur = ""
cnxConnexion..BaseDeDonnées = ""
cnxConnexion..Utilisateur = ""
cnxConnexion..MotDePasse = ""
cnxConnexion..Provider = hAccèsHF7
cnxConnexion..Accès = hOLectureEcriture
cnxConnexion..OptionsCurseur = hCurseurClient
cnxConnexion..InfosEtendues = ""
cnxConnexion..Cryptage = hCryptageRC5_16
// Affectation de la connexion à tous les fichiers de données
HChangeConnexion("*", cnxConnexion)
// Fixe le mot de passe utilisé pour créer ou pour ouvrir les fichiers de données
SI PAS (sHPasse ~= "") ALORS
HPasse("*", sHPasse)
FIN
// Ouverture de la connexion
HOuvreConnexion(cnxConnexion)
// Désactive la gestion des triggers applicatifs
HGèreTrigger(Faux)
// Modifie le chemin d'accès à la BDD
RENVOYER HChangeRep("*", ComplèteRep(sCheminBDD))
PROCÉDURE fLance(LOCALE sCheminEtNomDuFichier est une chaîne)
// Ouvre le document dans une nouvelle fenêtre non modale (traitement de texte, tableur, lecteur PDF ou éditeur d'images)
SI (OuvreDocument(sCheminEtNomDuFichier, "", odOptionFenêtreMaxi) = odEchec) ALORS
// Ouvre directement le document dans son application associée
SI PAS LanceAppliAssociée(sCheminEtNomDuFichier, "open") ALORS
// Ouvre la fenêtre "Ouvrir avec..."
LanceAppli("rundll32.exe shell32.dll, OpenAs_RunDLL """+ sCheminEtNomDuFichier +"""")
FIN
FIN
Un exemple d'utilisation :
fLance( "C:\logo.jpg" )
fLance(fRepExe() +[fSep()]+ "logo.jpg")
PROCÉDURE fSélectionne(LOCALE sExtensionParDefaut est une chaîne = "jpg") : booléen
sCheminEtNomDuFichier est une chaîne
SI PAS (sExtensionParDefaut ~= "jpg") ALORS
// Ouvre le sélecteur de fichiers avec les extensions de fichier
sCheminEtNomDuFichier = fSélecteur("", "", "Sélectionnez un fichier...", "Tous les fichiers (*.*)" +TAB+ "*.*" +RC+ "Traitement de texte" +TAB+ "*.doc;*.docx;*.odt;*.rtf" +RC+ "Tableur" +TAB+ "*.xls;*.xlsx;*.ots;*.csv" +RC+ "Document PDF" +TAB+ "*.pdf" +RC+ "Image" +TAB+ "*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.svg", sExtensionParDefaut)
SINON
// Ouvre le sélecteur d'image uniquement avec les extensions de fichier "Image"
sCheminEtNomDuFichier = fSélecteurImage("", "", "Sélectionnez une image...")
SI PAS fEstUneImage(sCheminEtNomDuFichier) ALORS
RENVOYER faux
FIN
FIN
// Teste l'existence du fichier externe
RENVOYER fFichierExiste(sCheminEtNomDuFichier)
Un exemple d'utilisation :
fSélectionne("jpg")
fSélectionne("docx")
fSélectionne("")
1) Tester la requête SQL directement dans le SGBD pour mesurer son temps d'exécution :
- copier / coller le code de la requête SQL dans le SGBD
- remplacer à la main les paramètres nécessaire pour la requête SQL
- lancer l'exécution de la requête SQL
- si le temps d'exécution est trop important alors modifier la requête SQL
2) Tester la requête SQL dans l'éditeur de requête pour mesurer son temps d'exécution :
- créer une nouvelle requête puis copier / coller le code SQL dans l'éditeur de requête
- faire un Go uniquement de la requête (tester l'élément) puis saisir les paramètres demandés et nécessaire
- si le temps d'exécution est trop important alors vérifier la connexion avec la base de données (matériel, réseau, port, anti-virus, etc...) et vérifier également l'utilisation de fonctions qui peuvent ralentir l'exécution comme par exemple HSimuleRéseau(), HTTPSimuleRéseau() ou HSécurité()
3) Dans la fenêtre du champ table :
- utiliser les propriétés ..AffichageActif et ..TotauxActif :
<NomChampTable>..AffichageActif = Faux
<NomChampTable>..TotauxActif = Faux
// Exécution de la requête SQL
// Traitements : TableAffiche(), etc...
<NomChampTable>..TotauxActif = Vrai
<NomChampTable>..AffichageActif = Vrai
- vérifier le mode d'initialisation de la requête : hRequeteSansCorrection / hRequeteSansCorrectionHF / etc...
- vérifier le temps d'exécution cumulé de tous les événements du champ table, principalement : "Affichage d'une ligne" et "Fin d'initialisation"
- utiliser l'analyseur de performance dans le traitement lent (fonctions AnalyseurDébut() et AnalyseurFin()) afin de cibler précisement le traitement qui provoque la lenteur
Il y a 2 pré-requis avant d'utiliser le code ci-dessous, il faut :
- se positionner sur l'enregistrement en utilisant une fonction H* (exemple : HLitRecherche, HLitRecherchePremier, ...)
- utiliser le SGBD HFSQL
// sNomTable : table de la base de données
// sNomRubrique : nom de la rubrique de type mémo binaire
// sCheminDossier : chemin / répertoire où sera extrait le fichier externe
PROCÉDURE HExtraitMémo_HFSQL(LOCALE sNomTable est une Source de Données, LOCALE sNomRubrique est une chaîne, LOCALE sCheminDossier est une chaîne) : chaîne
// Se positionner sur l'enregistrement en utilisant une fonction H* (exemple : HLitRecherche, HLitRecherchePremier, ...)
// Ne fonctionne que sous HFSQL
// Récupère les informations du mémo
sHInfoMémo est une chaîne = HInfoMémo(sNomTable, sNomRubrique)
// S'il existe un mémo binaire avec des informations disponibles
SI (sHInfoMémo <> "") _ET_ (ExtraitChaîne(sHInfoMémo, 2, TAB) <> "") ALORS
// S'il existe un mémo binaire avec des informations disponibles
sCheminFichier est une chaîne = ComplèteRep(sCheminDossier) + fExtraitChemin(ExtraitChaîne(sHInfoMémo, 2, TAB), fFichier+fExtension)
QUAND EXCEPTION DANS
HExtraitMémo(sNomTable, sNomRubrique, sCheminFichier)
FAIRE
ExceptionPropage() // Exceptions possibles : "Répertoire inexistant", "Fichier déjà ouvert par une application", ...
SINON
SI fFichierExiste(sCheminFichier) ALORS
RENVOYER sCheminFichier
FIN
FIN
FIN
RENVOYER ""
Un exemple d'utilisation :
SI HLitRecherchePremier(SOCIETE, IDSOCIETE, 3) ALORS
sCheminFichier est une chaîne = HExtraitMémo_HFSQL(SOCIETE, SOCIETE.Logo..Nom, fRepExe())
LanceAppliAssociée(sCheminFichier)
FIN
En complément de l'aide pour l'Instruction POUR TOUT (parcours de tableaux) .
tabChaîne est un tableau de chaîne
TableauAjoute(tabChaîne, "UneValeur1")
TableauAjoute(tabChaîne, "UneValeur2")
TableauAjoute(tabChaîne, "UneValeur3")
// tabChaîne = ["UneValeur1", "UneValeur2", "UneValeur3"]
// Parcourir un tableau à une dimension
POUR TOUT sValeur, nIndice, nCompteur DE tabChaîne
Trace(nCompteur, nIndice, sValeur)
FIN
// Le résultat de la trace vaut :
// 1 1 UneValeur1
// 2 2 UneValeur2
// 3 3 UneValeur3
En complément de l'aide pour l'Instruction POUR TOUT (parcours de tableaux associatifs) .
taTableau est un tableau associatif (ccSansCasse+ccSansAccent+ccSansEspace, "", wlChaîne) de chaîne
// avec (Combinaison d'options, Valeur par défaut, Type de clé)
taTableau["UneCle1"] = "UneValeur1"
taTableau["UneCle2"] = "UneValeur2"
SI taTableau["UneCle3"]..Vide ALORS // Remplir si la clé n'est pas déjà présente
taTableau["UneCle3"] = "UneValeur3"
FIN
// taTableau = [["UneCle1", "UneValeur1"], ["UneCle2", "UneValeur2"], ["UneCle3", "UneValeur3"]]
// Parcourir un tableau associatif (clé, valeur)
POUR TOUT sValeur, sCle, nCompteur DE taTableau
Trace(nCompteur, sCle, sValeur)
FIN
// Le résultat de la trace vaut :
// 1 UneCle1 UneValeur1
// 2 UneCle2 UneValeur2
// 3 UneCle3 UneValeur3
Le mot-clé MesParamètres permet de manipuler tous les paramètres d'une procédure.
Par exemple, la procédure ci-dessous calcule la somme de ses 3 paramètres en entrée :
PROCÉDURE CalculerSomme(nX est un entier, nY est un entier, nZ est un entier) : entier
nSomme est un entier
POUR i = 1 _À_ MesParamètres..Occurrence
nSomme += MesParamètres[i] // nSomme = nSomme + MesParamètres[i]
FIN
RENVOYER nSomme
FIN
// Le résultat de la trace vaut 7
Trace( CalculerSomme(1, 2, 4) )
L'exemple ci-dessus est une procédure avec seulement 3 paramètres.
Et donc la procédure peut être écrite simplement sans utiliser le mot-clé MesParamètres :
PROCÉDURE CalculerSomme(nX est un entier, nY est un entier, nZ est un entier) : entier
RENVOYER (nX + nY + nZ)
FIN
Mais pourrait-on faire une procédure générique qui calcule la somme de tous ses paramètres sans en connaitre le nombre exact ? Oui, grâce à l'utilisation du signe "*".
// Le nombre de paramètres passé à la procédure est variable
PROCÉDURE CalculerSomme(*) : entier
nSomme est un entier
POUR i = 1 _À_ MesParamètres..Occurrence
nSomme += MesParamètres[i]
FIN
RENVOYER nSomme
FIN
// Le résultat de la trace vaut 15
Trace( CalculerSomme(1, 2, 4, 8) )
L'exemple ci-dessus est une procédure avec des paramètres ayant le même type (entier).
Et donc la procédure peut être écrite simplement sans utiliser le mot-clé MesParamètres :
PROCÉDURE CalculerSomme(tabEntier est un tableau d'entier) : entier
RENVOYER Somme(tabEntier)
FIN
// Le résultat de la trace vaut 15
Trace( CalculerSomme([1, 2, 4, 8]) )
Le mot-clé MesParamètres est puissant car il accepte aussi les paramètres de type différent. Et ils peuvent directement être passés à une autre procédure / fonction.
Ces paramètres variables apportent un grand nombre de possibilités afin de rendre les traitements génériques.
PROCÉDURE OuvrirFenetre(sNomFenêtre est une chaîne <nom de fenêtre>, *)
Trace(sNomFenêtre)
Ouvre(sNomFenêtre, MesParamètres[2 A])
FIN
La fonction LigneCommande() correspond à l'argument "char *argv[]" en langage C++.
A compléter avec les fonctions ProjetInfo(piNomProjet) et exeInfo(exeNom).
Nous allons créer un algorithme simple qui calcule la somme de deux nombres.
Cette procédure attend deux paramètres (nombre1 et nombre2) et renvoie la somme des 2 paramètres grâce à l'opérateur +.PROCÉDURE AdditionnerDeuxNombres(nombre1, nombre2)
RENVOYER nombre1 + nombre2
Le résultat '3' est bien celui attendu. C'est-à-dire que la variable nommée 'laSomme' vaut 3.soit laSomme = AdditionnerDeuxNombres(1, 2)
Info("La somme de 1+2 vaut : "+laSomme)
// La somme de 1+2 vaut : 3
Le résultat est "ab" car l'opérateur + permet aussi la concaténation de 2 chaînes.soit laSomme = AdditionnerDeuxNombres("a", "b")
Info("La somme de 'a'+'b' vaut : "+laSomme)
// La somme de 'a'+'b' vaut : ab
Nous venons d'ajouter un type aux paramètres de la procédure. Grâce à cela, la procédure nommée "AdditionnerDeuxNombres" attend réellement 2 numériques.PROCÉDURE AdditionnerDeuxNombres(nombre1 est un numérique, nombre2 est un numérique)
RENVOYER nombre1 + nombre2
Le programme essaie de convertir les caractères "a" et "b" en numérique. Il n'y arrive pas donc dans la procédure, les variables nombre1 et nombre2 vaut 0 (zéro), d'où le résultat final 0 (zéro).soit laSomme = AdditionnerDeuxNombres("a", "b")
Info("La somme de 'a'+'b' vaut : "+laSomme)
// La somme de 'a'+'b' vaut : 0
Nous pouvons aller plus loin en précisant le type de retour de la procédure.soit laSomme = AdditionnerDeuxNombres("1", "2")
Info("La somme de '1'+'2' vaut : "+laSomme)
// La somme de '1'+'2' vaut : 3
Rajouter le type de retour d'une procédure est une bonne pratique. Si le code source de la procédure est longue de plusieurs lignes alors un développeur pourra facilement savoir le type de retour de la procédure en observant simplement sa déclaration et sa signature :PROCÉDURE AdditionnerDeuxNombres(nombre1 est un numérique, nombre2 est un numérique) : numérique
RENVOYER nombre1 + nombre2
PROCÉDURE AdditionnerDeuxNombres(nombre1 est un numérique, nombre2 est un numérique) : numérique
D'un seul coup d'oeil, le développeur sait que la procédure additionne 2 nombres grâce au nom de la procédure. De plus, elle attend 2 numériques en paramètre (nombre1 et nombre2) et elle retourne un numérique. Ainsi nous pouvons également mieux utiliser cette procédure en ajoutant le type de la variable (numérique) retourné :// Résumé : Calcule la somme de 2 nombres.
// Paramètres :
// xNombre1 (numérique) : le 1er nombre
// xNombre2 (numérique) : le 2ème nombre
// Valeur de retour :
// numérique : somme des 2 paramètres (1er nombre + 2ème nombre)
//
PROCÉDURE AdditionnerDeuxNombres(xNombre1 est un numérique, xNombre2 est un numérique) : numérique
RENVOYER (xNombre1 + xNombre2)
De plus comme nous avons utilisé le type de variable "Numérique", les nombres décimaux avec virgule sont gérés :xLaSomme est un numérique = AdditionnerDeuxNombres(1, 2)
Info("La somme de '1'+'2' vaut : "+xLaSomme)
// La somme de '1'+'2' vaut : 3
Si nous avions utilisé le type de variable "Entier" au niveau des paramètres de cette fonction alors les nombres décimaux avec virgule n'auraient pas été gérés. Et la fonction auraient renvoyée une valeur incohérente.xLaSomme est un numérique = AdditionnerDeuxNombres(1.2, 2.3)
Info("La somme de '1.2'+'2.3' vaut : "+xLaSomme)
// La somme de '1.2'+'2.3' vaut : 3.5
PROCÉDURE AdditionnerDeuxEntiers(nNombre1 est un entier, nNombre2 est un entier) : entier
RENVOYER (nNombre1 + nNombre2)
Ajouter le mot-clé LOCALE car les paramètres xNombre1 et xNombre2 ne sont pas modifiés dans la procédure et ne doivent pas l’être.nLaSomme est un numérique = AdditionnerDeuxEntiers(1.4, 2.3)
Info("La somme de '1.4'+'2.3' vaut : "+nLaSomme)
// La somme de '1.4'+'2.3' vaut : 3 - la valeur est incohérente : 1.4+2.3 <> 3
PROCÉDURE AdditionnerDeuxNombres(LOCALE xNombre1 est un numérique, LOCALE xNombre2 est un numérique) : numérique
RENVOYER (xNombre1 + xNombre2)
// Tester avec des nombres :
// Test avec résultat positif et l'élément neutre (zéro)
dbgVerifieEgalite( AdditionnerDeuxNombres(1, 2), 3)
dbgVerifieEgalite( AdditionnerDeuxNombres(2, 1), 3) // Commutativité de l'addition
dbgVerifieEgalite( AdditionnerDeuxNombres(4, 0), 4)
dbgVerifieEgalite( AdditionnerDeuxNombres(0, 4), 4) // Commutativité de l'addition
// Test avec résultat négatif
dbgVerifieEgalite( AdditionnerDeuxNombres(-1, -2), -3)
dbgVerifieEgalite( AdditionnerDeuxNombres(-2, -1), -3) // Commutativité de l'addition
// Test avec résultat décimal
dbgVerifieEgalite( AdditionnerDeuxNombres(1.2, 2.3), 3.5)
dbgVerifieEgalite( AdditionnerDeuxNombres(2.3, 1.2), 3.5) // Commutativité de l'addition
dbgVerifieEgalite( AdditionnerDeuxNombres(-1.2, -2.3), -3.5)
dbgVerifieEgalite( AdditionnerDeuxNombres(-2.3, -1.2), -3.5) // Commutativité de l'addition
// Test avec résultat à 0 (zéro)
dbgVerifieEgalite( AdditionnerDeuxNombres(1, -1), 0)
dbgVerifieEgalite( AdditionnerDeuxNombres(-1, 1), 0) // Commutativité de l'addition
dbgVerifieEgalite( AdditionnerDeuxNombres(0, 0), 0)
// Tester avec des chaînes :
// Test avec résultat positif et l'élément neutre (zéro)
dbgVerifieEgalite( AdditionnerDeuxNombres("1", "2"), 3)
dbgVerifieEgalite( AdditionnerDeuxNombres("2", "1"), 3) // Commutativité de l'addition
dbgVerifieEgalite( AdditionnerDeuxNombres("4", "0"), 4)
dbgVerifieEgalite( AdditionnerDeuxNombres("0", "4"), 4) // Commutativité de l'addition
// Test avec résultat négatif
dbgVerifieEgalite( AdditionnerDeuxNombres("-1", "-2"), -3)
dbgVerifieEgalite( AdditionnerDeuxNombres("-2", "-1"), -3) // Commutativité de l'addition
// Test avec résultat décimal
dbgVerifieEgalite( AdditionnerDeuxNombres("1.2", "2.3"), 3.5)
dbgVerifieEgalite( AdditionnerDeuxNombres("2.3", "1.2"), 3.5) // Commutativité de l'addition
dbgVerifieEgalite( AdditionnerDeuxNombres("-1.2", "-2.3"), -3.5)
dbgVerifieEgalite( AdditionnerDeuxNombres("-2.3", "-1.2"), -3.5) // Commutativité de l'addition
// Test avec résultat à 0 (zéro)
dbgVerifieEgalite( AdditionnerDeuxNombres("1", "-1"), 0)
dbgVerifieEgalite( AdditionnerDeuxNombres("-1", "1"), 0) // Commutativité de l'addition
dbgVerifieEgalite( AdditionnerDeuxNombres("0", "0"), 0)
// Test avec caractère et chaîne
dbgVerifieEgalite( AdditionnerDeuxNombres("a", "b"), 0)
dbgVerifieEgalite( AdditionnerDeuxNombres("nombre", "chiffre"), 0)
// A FAIRE : Tests aux extrémités (positif et négatif) : ajouter "est un numérique (*)"