Ecrire une fonction proprement

cargocargo Membre
03:05 modifié dans API AppKit #1
Quelle est la meilleure façon d'écrire une fonction ?

Dans le cas d'un "if", faut-il rajouter un "else" qui ne renvoit rien en cas de problème avec les conditions ? Comme ceci :
<br />...<br />if (condition)<br />{<br />return uneValeur; <br />}<br />else (condition)<br />{<br />return uneAutreValeur; <br />}<br />else<br />{<br />return; <br />}<br />...<br />


Faut-il démarrer un calcul avec des variables "nil" en cas de problème au niveau du calcul de la valeur ?
Comme ceci :
<br />...<br />maVariable = nil&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  // Mise à  &quot;zéro&quot;<br />maVariable = 1+1&nbsp; &nbsp; &nbsp; &nbsp; // Calcul de la variable<br />return maVariable<br />...<br />


La question est : est-ce utile et pourquoi ?
Si vous avez aussi d'autres suggestions pour un code plus propre, n'hésitez pas...En fait un petit tuto sur l'écriture de fonctions, les différents types de fonctions, la gestion des releases autoreleases, toutes les choses à  ne pas oublier quand on écrit du code, les astuces, etc..., ça serait pas mal.
Je veux bien m'y coller si on m'indique où trouver de l'info.

Réponses

  • 03:05 modifié #2
    dans 1176968439:

    Dans le cas d'un "if", faut-il rajouter un "else" qui ne renvoit rien en cas de problème avec les conditions ?


    Dans ce cas là , les else ne servent absolument à  rien, du fait qu'un return arrête l'exécution de la fonction. On peut donc se contenter de:

    [tt]if (cond1) return val1;
    if (cond2) return val2;

    return defaultValue;[/tt]

    Mais il toujours que la fonction renvoie quelque chose.

    Pour la deuxième question, ça dépend des cas.

    Sinon regarde la section truc&astuce de ce forum, il y a quelques trucs utiles.
  • schlumschlum Membre
    avril 2007 modifié #3
    Tu as un exemple concret ?  ???

    Attention, "nil" ne correspond qu'aux pointeurs sur objets et "Nil" aux pointeurs sur classes, NULL aux autres pointeurs C (pointeurs char* en C++/Objective-C++, 0 pour les autres pointeurs).

    Quand il y a une seule instruction dans le "if", on peut virer les accolades, c'est plus lisible je trouve.

    Et s'il y a beaucoup de :

    if(var==val1||var==val2)<br />...<br />else if(var==val3)<br />...<br />else if(var==val4||var==val5)<br />...<br />else<br />...
    


    -> Préférer un switch / case
  • cargocargo Membre
    03:05 modifié #4
    Mais il toujours que la fonction renvoie quelque chose.

    C'est bien ça ma question : dans le cas où j'ai deux conditions et dans un fonctionnement normal je suis forcément dans l'une ou dans l'autre condition ("pile" ou "face"), est-ce qu'il faut systématiquement faire un return avec une defaultValue en cas de dysfonctionnement de l'appli ?

    Toujours avec 2 conditions du genre "pile" ou "face" est-ce qu'un if sur une condition suffit ou est-ce qu'il est préférable d'examiner les 2 conditions et de faire un return defaultValue.
    De toutes façons si l'appli arrive pas à  examiner l'une ou dans l'autre condition et faire les calculs afférents elle va pas se bloquer, ou faire un loop ou un truc comme ça elle va renvoyer un objet nil non ?

    Mon intuition me dit qu'il faut systématiquement renvoyer une defaultValue au cas où mais bon...


    Quand il y a une seule instruction dans le "if", on peut virer les accolades, c'est plus lisible je trouve.

    Du coup mon .m a fait une cure weight watchers... ;)
  • schlumschlum Membre
    03:05 modifié #5
    dans 1176977801:

    Mon intuition me dit qu'il faut systématiquement renvoyer une defaultValue au cas où mais bon...


    Yep ! Tu mets simplement "return defaultValue;" à  la fin de ta fonction (s'il peut y avoir des cas non traités... De toute façon, le compilateur ne sera pas content du tout s'il trouve des cas sans "return" !)
  • AliGatorAliGator Membre, Modérateur
    03:05 modifié #6
    dans 1176978550:

    dans 1176977801:

    Mon intuition me dit qu'il faut systématiquement renvoyer une defaultValue au cas où mais bon...


    Yep ! Tu mets simplement "return defaultValue;" à  la fin de ta fonction (s'il peut y avoir des cas non traités... De toute façon, le compilateur ne sera pas content du tout s'il trouve des cas sans "return" !)
    Heu la seule règle à  respecter, et de toute façon le compilo gueulera si tu ne la respectes pas, c'est que si ta fonction doit retourner une valeur, tous les chemins possibles empruntés dans ta fonction doivent contenir un "return qqch". C'est à  dire que quelle que soit la branche des if/else et autres switch/case qui sera empruntée, il faut qu'il rencontre à  un moment donné un return.
    Ce return peut être unique dans ta fonction, à  la toute fin de cette dernière, ou il peut y en avoir plusieurs (pour retourner une valeur différente selon les conditions), du moment qu'il n'y ait pas de cas où il ne sache pas quoi retourner.

    Par contre bien sûr si elle n'est pas sensé retourné de valeur (void), pas besoin de return.

    Donc si tu as un
    if (condition)<br />&nbsp; return 1;<br />else<br />&nbsp; return 2;
    
    Alors pas besoin de mettre un 3e "return defaultValue" à  la fin.
  • schlumschlum Membre
    03:05 modifié #7
    Ben... C'est ce que j'ai dit, non ?  :)
  • MalaMala Membre, Modérateur
    03:05 modifié #8
    dans 1176968439:

    Dans le cas d'un "if", faut-il rajouter un "else" qui ne renvoit rien en cas de problème avec les conditions ? Comme ceci :
    <br />...<br />if (condition)<br />{<br />return uneValeur; <br />}<br />else (condition)<br />{<br />return uneAutreValeur; <br />}<br />else<br />{<br />return; <br />}<br />...<br />
    


    D'expérience, préférer un seul return par fonction/méhode c'est plus facile à  maintenir par la suite mais ce n'est que mon avis.

    dans 1176968439:

    Faut-il démarrer un calcul avec des variables "nil" en cas de problème au niveau du calcul de la valeur ?

    Initialiser systématiquement les variables et surtout les pointeurs permet d'éviter des violations d'accès et de trouver plus facilement les erreurs de codage lors d'un debug. Pour du code multi-compilateur, tu peux aussi trouver des différences de gestion (certains initialisent par défaut la mémoire à  zéro et d'autres pas). C'est donc bien souvent plus une question de prudence et de commodité que de réelle nécessité. Repasser un pointeur à  nil après libération d'une instance est aussi un excellent réflexe (et c'est très commode quand on fait une passe en debug pour voir d'un coup d'oeil ce qui est encore alloué et ce qui ne l'est plus).

    dans 1176970159:

    seule instruction dans le "if", on peut virer les accolades, c'est plus lisible je trouve.

    C'est amusant moi c'est le contraire. Je trouve plus lisible de mettre des accolades systématiquement.  :)
  • schlumschlum Membre
    03:05 modifié #9
    dans 1177025407:

    C'est amusant moi c'est le contraire. Je trouve plus lisible de mettre des accolades systématiquement.  :)

    Ben en C, les {} signalent un "groupement" d'instructions... Une seule instruction ça fait effectivement un groupement, mais bon...
    Tant que les indentations sont bien faites...  :)
  • MalaMala Membre, Modérateur
    03:05 modifié #10
    dans 1177055723:

    Tant que les indentations sont bien faites...  :)

    C'est clair.  :P
  • 03:05 modifié #11
    Perso je ne mets pas les accolades uniquement si la condition et l'instruction peuvent tenir sur la même ligne. Si c'est sur 2 lignes, je mets d'office les accolades, histoire ne pas avoir de mauvaise surprise si je mets un log avant l'instruction.

    dans 1176977801:

    Mon intuition me dit qu'il faut systématiquement renvoyer une defaultValue au cas où mais bon...

    Plutôt que de renvoyer une "defaultValue", renvoie plutôt une valeur que tu pourrais utiliser pour indiquer une erreur (genre NSNotFound si tu dois rechercher un index).

    dans 1177025407:

    D'expérience, préférer un seul return par fonction/méhode c'est plus facile à  maintenir par la suite mais ce n'est que mon avis.

    Ce n'est que mon avis, mais c'est le genre de cas où un bon éditeur de texte peut faire la différence. Enfin, pour ce cas-ci, la qualité du bon éditeur de texte est juste de coloriser différement les mots clé "importants" - ou de permettre de personnaliser de la colorisation si ce n'est pas fait par défaut. J'ai configuré TextMate pour qu'il m'affiche les mots-clé importants en rouge vif et en gras  (j'ai inclus dans ce lot return, break, retain, release, autorelease), alors que le reste est plutôt dans les tons bleu-mauve, et avec ça, impossible de les rater.
  • MalaMala Membre, Modérateur
    03:05 modifié #12
    dans 1177098854:

    Ce n'est que mon avis, mais c'est le genre de cas où un bon éditeur de texte peut faire la différence.

    Oui, cela se défend tout à  fait. Je dois avouer qu'ayant fait mes armes sous VI (pas vraiment l'éditeur où la coloration syntaxique était de rigueur :) ) les automatismes restent et du coup je m'attend toujours à  trouver mes return à  leur à  la même place.  :P

    dans 1177098854:

    Perso je ne mets pas les accolades uniquement si la condition et l'instruction peuvent tenir sur la même ligne.

    Ca moi j'évite parce que je trouve ça plus pénible quand je fais une passe en pas à  pas.

    C'est là  qu'on voit bien qu'on a tous nos petits trucs et habitudes pratiques.  :P
  • schlumschlum Membre
    03:05 modifié #13
    Pareil... Je déteste avoir plusieurs instructions sur une seule ligne  :)
  • tabliertablier Membre
    03:05 modifié #14
    Nous avons tous nos petites manies!  ::)

    Pour ma part, j'évite de mettre plusieurs intruction par ligne. Je sépare systématiquement les déclarations de variables de leur initialisation et je garde l'habitude prise en C de mettre les déclarations (sans tabulation) au début des routines (pardon: des méthodes). Quand aux lignes dans les if et autre mots clef de condition je les tabule systématiquement car l'aspect de l'écriture contribue pour moi à  la compréhension du fonctionnement.
    cela donne un aspect comme ci-dessous:
    // vérifie qu&#39;il n&#39;y a pas d&#39;imbrication entre deux dossiers. <br />//<br />- (NSNumber *)testDossiers:(NSString *)source :(NSString *)destin<br />{<br />NSRange tt, uu ;<br /><br />	tt = [source rangeOfString:destin] ;<br />	uu = [destin rangeOfString:source] ;<br />	if ( (tt.length + uu.length) != 0) <br />	&nbsp; &nbsp;  return [NSNumber numberWithBool:YES] ;<br />	else<br />	&nbsp; &nbsp;  return [NSNumber numberWithBool:NO] ;<br />}<br />
    


    bien sur, cela peut s'écrire en une seule ligne! mais je ne trouve pas cela explicite (et je n'ai pas essayé):

    // vérifie qu&#39;il n&#39;y a pas d&#39;imbrication entre deux dossiers. <br />//<br />- (NSNumber *)testDossiers:(NSString *)source :(NSString *)destin<br />{<br />&nbsp; &nbsp;  return [NSNumber numberWithBool:([source rangeOfString:destin].length + [destin rangeOfString:source].length) == 0 ] ;<br />}<br />
    



  • 03:05 modifié #15
    dans 1177100357:

    dans 1177098854:

    Perso je ne mets pas les accolades uniquement si la condition et l'instruction peuvent tenir sur la même ligne.
    Ca moi j'évite parce que je trouve ça plus pénible quand je fais une passe en pas à  pas.


    Petite précision: tenir sur une ligne veut dire pour moi ne pas dépasser les 80 caractères (faut pas abuser non plus). Donc autant dire que ça n'arrive que pour des cas simplissimes qui ne causent que peu de problèmes dans les pas-à -pas.

    dans 1177105861:
    Quand aux lignes dans les if et autre mots clef de condition je les tabule systématiquement car l'aspect de l'écriture contribue pour moi à  la compréhension du fonctionnement.


    ça tu n'es pas le seul, l'indentation automatique fait maintenant partie des fonctions de base d'un éditeur de texte et ça fait partie des cas où la tabulation est ajoutée de manière automatique (ceci dit, je pense que l'indentation automatique est désactivée par défaut dans Xcode).
  • WIMPWIMP Membre
    03:05 modifié #16
    Pour une condition de type pile face, autrement dit YES NO, le C a prévu l'expression conditionnelle, qui peur remplacer une fonction, et que je trouve pratique, même si la lecture du code peut parfois sembler hermétique:

    En pseudo code:
    x = question ? valeur si OUI : valeur si NON

    <br />if (a &gt; b)<br />	x = a;<br />else<br />	x = b;<br />
    


    est équivalent à 

    <br />x = (a &gt; b) ? a : b;<br />
    
  • avril 2007 modifié #17
    Et il y a aussi une variante peu connue de cette notation:

    valeur1 ?: valeurSiValeur1EstNulle

    Si valeur1 n'est pas nulle, c'est évidemment valeur1 qui est renvoyée.
  • KansasKansas Membre
    03:05 modifié #18
    Bonjour à  tous,

    Comme vous parler de fonction, j'avais une question assez bête vu mon niveau en programmation.
    Est-ce qu'une fonction peut elle retourner plusieurs valeurs?

    Merci d'avance de vos réponse.
  • muqaddarmuqaddar Administrateur
    03:05 modifié #19
    dans 1244213178:

    Bonjour à  tous,

    Comme vous parler de fonction, j'avais une question assez bête vu mon niveau en programmation.
    Est-ce qu'une fonction peut elle retourner plusieurs valeurs?

    Merci d'avance de vos réponse.


    Oui si tu utilises un dictionnaire avec les valeurs à  retourner.
    Donc tu retournes un dictionnaire de valeurs, mais un seul objet.
  • Philippe49Philippe49 Membre
    03:05 modifié #20
    ou un pointeur sur la première valeur si toutes les valeurs sont de même type C,
    ou une structure en C,
    ou toute classe objective-C qui encapsule plusieurs valeurs.
  • KansasKansas Membre
    03:05 modifié #21
    Ok merci pour vos réponse
  • schlumschlum Membre
    03:05 modifié #22
    À savoir que pour le retour d'une structure, si cette structure est de taille > sizeof(void*), gcc transforme en interne l'appel "maStruct maFonc(...)" en "void maFonc(maStruct *ptr,...)" pour éviter la recopie mémoire supplémentaire.
  • AliGatorAliGator Membre, Modérateur
    03:05 modifié #23
    Et en Objective-C c'est pareil, le compilateur transforme une méthode ObjC qui retourne une structure en appel à  la fonction objc_msgSend_stret (alors que si ça retourne un type classique genre un entier, ça transforme juste en objc_msgSend).

    Mais tout ça c'est de la tambouille sous le capot dont on n'a pas besoin de se soucier normalement. On retourne juste un objet "complexe" (qui contient potentiellement plusieurs variables membres, retourner un NSDictionary, comme l'a mentionné muqaddar, étant très courant).

    On peut aussi retourner un NSArray, ça marche aussi, mais ça suppose qu'on sait dans quel ordre sont retournés les arguments, plutôt que de leur "donner un nom" en les associant à  une clé de dictionnaire, donc "ça fait moins joli".


    Dernière solution, passer les arguments "secondaires" à  retourner par pointeur, mais là  ça fait bcp moins "objet" comme façon de faire. Le seul cas où c'est assez courant en Cocoa c'est pour quand une méthode permet de récupérer une NSError* si jamais l'opération effectuée par la fonction a échoué, par exemple. Ce qui nous intéresse dans 80% des cas c'est le résultat classique de la fonction, mais si jamais on veut tester les cas d'erreurs, le NSError est passé par pointeur en paramètre nous permettant de récupérer cette dernière si besoin.
    C'est le cas par exemple de méthodes comme [tt]-(NSString*)initWithContentsOfFile:(NSString*)path encoding:(NSStringEncoding)enc error:(NSError**)error[/tt] : la valeur de retour "principale", celle qui nous intéresse la plupart du temps, c'est la NSString retournée par la fonction, correspondant au contenu du fichier se trouvant à  l'emplacement path[/t]... Mais si jamais il y a eu une erreur lors de la lecture (pb de droits d'accès, fichiers inexistant, ...), la fonction va retourner "nil" comme NSString, n'ayant pas réussi à  initialiser la chaà®ne... et va alors "retourner" la NSError décrivant l'erreur dans le paramètre "error" passé par référence, si celui-ci n'est pas NULL.


    Après ça reste une possibilité donc de passer les paramètres par référence pour les faire remplir par la fonction mais ça reste peu courant et moins utilisé que de retourner un NSDictionary ou un objet perso encapsulant les différentes valeurs à  retourner.
Connectez-vous ou Inscrivez-vous pour répondre.