IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langages de programmation Discussion :

Y a-t-il une raison pour laquelle les fonctions ne retournent qu'une seule valeur ?


Sujet :

Langages de programmation

  1. #21
    Membre éclairé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2008
    Messages
    163
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 59
    Localisation : France, Calvados (Basse Normandie)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Février 2008
    Messages : 163
    Points : 710
    Points
    710
    Par défaut
    d'accord avec heid, ça permet "normalement" de canaliser "un peu" les developpeurs.
    Maintenant, pour des langages de script, pourquoi pas. (je l'utilise un peu avec lua)
    Je travaille aussi sur AS/400, le gros reproche qu'on fait aux CLP, RPG & COBOL, c'est de pouvoir appeler des prrogrammes en passant n paramètres qui peuvent etre lus en retour. (eq fonction multi retours)
    Et bien, pour l'avoir vécu les amis, ça n'eclairci pas, mais alors pas du tout le code.
    Comme toujours, ils y a les dev "propres" et les "gorets", quelque soit le langage . . .

  2. #22
    Expert éminent sénior Avatar de Uther
    Homme Profil pro
    Tourneur Fraiseur
    Inscrit en
    Avril 2002
    Messages
    4 562
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pyrénées Orientales (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Tourneur Fraiseur

    Informations forums :
    Inscription : Avril 2002
    Messages : 4 562
    Points : 15 493
    Points
    15 493
    Par défaut
    Citation Envoyé par anonyme Voir le message
    La question est inhérente à l'architecture des processeurs (x86 par exemple et en l'occurence) et à la gestion des opcode call/ret (assembler mnemonic). C'est une question de taille de registre et de pile de retour.
    Je vois pas en quoi c'est un problème. call/ret ne font que du branchage, ils laissent libre de traiter les données en entrée comme en sortie comme on le souhaite que se soit sur la pile (en utilisant la taille que l'on souhaite) ou via les registres si leur taille convient.

  3. #23
    Membre à l'essai
    Profil pro
    Inscrit en
    Janvier 2003
    Messages
    6
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2003
    Messages : 6
    Points : 15
    Points
    15
    Par défaut
    En fait, ça se fait de plus en plus avec les nouveaux langages qui poussent (Scala, Kotlin, etc.).
    Je viens de trouver un wiki qui ne parle que de ça : http://rosettacode.org/wiki/Return_multiple_values

  4. #24
    Membre éclairé

    Homme Profil pro
    Développeur Java
    Inscrit en
    Février 2007
    Messages
    179
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2007
    Messages : 179
    Points : 653
    Points
    653
    Par défaut
    Citation Envoyé par sevyc64 Voir le message
    Sur ce point, on a tous pris l'habitude de donner des valeurs particulières au résultat (du calcul) pour faire passer l'information sur l’exécution de ce même calcul. Parce qu'on a pas vraiment le choix, mais conceptuellement c'est quand même une belle aberration. Qui pourrait être résolue pas les fonctions multi-retour
    Il existe les exceptions dans pas mal de langage pour ce cas là.
    Mais effectivement même avec les exceptions on a quand même tendance à mettre des NULL dans les retours (même si y'a des patern pour ne pas le faire en POO https://en.wikipedia.org/wiki/Null_Object_pattern)
    L'expérience est une lanterne que l'on porte sur le dos et qui n'eclaire jamais que le chemin parcouru.

    La nature fait les choses sans se presser, et pourtant tout est accompli.

  5. #25
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 749
    Points
    39 749
    Par défaut
    Citation Envoyé par anonyme Voir le message
    pour la valeur de ret, si on veut gérer des struct à deux valeurs y a KeyValuePair<int, int> et sinon Tuple<,..,> ou alors une classe/struct spécifique.
    Utiliser KeyValuePair<K,V> pour autre chose qu'une paire clé/valeur est une mauvaise idée, ça nuit à la lisibilité...
    Quand à Tuple<...>, le problème est que ses propriétés n'ont pas de nom descriptif (Item1, Item2...)
    Utiliser une structure ou classe spécifique est mieux, mais
    - d'une part c'est pénible d'avoir à la déclarer
    - d'autre part c'est difficile à nommer. Conceptuellement la fonction renvoie plusieurs valeurs, et le "truc" qui sert à les regrouper n'a aucune utilité fonctionnelle ; d'où la difficulté de trouver un nom pertinent

    Citation Envoyé par anonyme Voir le message
    en quoi écrire (var sum, var count) = Tally(myValues) est-il plus simple que var data = Tally(myValues) avec "KeyValuePair<int, int> Tally(myValues)" ou quelque chose du genre "SumCountStructOrClass Tally(myValues)" où SumCountStructOrClass est définit quelque part pour avoir un typage fort.
    - c'est mieux que KeyValuePair<K, V>, parce que KeyValuePair ne sert pas à ça et ne documente pas du tout les valeurs qui sont renvoyées
    - c'est mieux que Tuple<...>, parce que les propriétés ont des noms qui ont du sens, contrairement à Tuple<...> qui ne documente pas du tout les valeurs qui sont renvoyées
    - c'est mieux qu'une classe ou structure spécifique, parce que tu n'as pas à déclarer cette classe et à lui trouver un nom pertinent
    - c'est mieux que les paramètres out, parce que tu n'as pas besoin de les déclarer avant
    - contrairement à toutes les techniques citées ci-dessus, tu peux "unpacker" le tuple directement en l'affectant à plusieurs variables à la fois

    Citation Envoyé par anonyme Voir le message
    ici, (int sum, int count), est un struct ou une classe en orienté objet.
    Effectivement le compilateur génèrera un type anonyme pour englober ces valeurs. Et alors ? Son nom n'a aucune importance, c'est juste de la pollution dans ce cas précis.

    Citation Envoyé par anonyme Voir le message
    si on type pas fort, on peut faire n'importe quoi n'importe comment.
    Je ne vois pas en quoi on peut faire n'importe quoi n'importe comment... C'est toujours fortement typé, le type n'a juste pas de nom.

    Citation Envoyé par anonyme Voir le message
    écrire (int sum, int count) n'a aucun sens puisque sum et count sont des arbitraires de conception intermédiaire pour le design du code.
    Et ? Je ne vois pas bien le lien de causalité présenté dans ta phrase...

    Citation Envoyé par anonyme Voir le message
    à la rigueur, on pourrait pouvoir écrire : "public (int, int) Tally(IEnumerable<int> values) { ... }", et donc c'est un tuple ou une keyvaluepair.
    Je ne vois pas en quoi c'est mieux. Avec ça tu ne sais pas à quoi correspondent les valeurs (int, int). En quoi c'est un avantage ?

    Citation Envoyé par anonyme Voir le message
    écrire (int sum, int count) autoriserait le code de la méthode à utiliser ces variables prénommées...
    J'ai pas compris... sum et count ne sont pas des variables de la méthode, c'est juste les noms des membres du tuple que la méthode doit renvoyer

    Citation Envoyé par anonyme Voir le message
    alors autant créer une structure pour faire la chose proprement, genre "struct TallyResult { int sum, int count }".
    Ca pose toujours le problème de déclaration et de nommage d'un truc qui sert uniquement à transporter les données entre l'appelant et l'appelé

  6. #26
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par DonQuiche Voir le message
    Je ne suis pas sûr de ce que tu as voulu dire par là (*) mais les paramètres out fonctionnent exactement comme la valeur de retour normale : l'appelant fourni à l'appelé une adresse où écrire la valeur.

    (*) Les processeurs sont en général basés sur des registres et non pas selon une pile d'opérandes mais peut-être parlais-tu des registres de pile (qui sont avant tout utilisés pour préserver l'état d'une fonction avant d'en appeler une autre et non pas comme pile d'opérandes).
    Citation Envoyé par jopopmk Voir le message
    Perso, hors extension de fonctionnalités de base, j'ai besoin d'un seul retour : un code (flag|flag, true/false ou strerr ... y'en a pour tous les goûts). Sinon toutes mes variables à modifier sont en paramètre. Je suis dans le "contournement" décrit par sevyc64, mais le résultat est là. Du coup je vois pas trop l'utilité de vouloir à tout prix retourner des compositions non structurées.
    désolé pour les imprécisions voire les erreurs, ça fait très longtemps que j'ai pas fait d'assembleur et jamais d'IL, mais l'idée est là. en assembleur x86, la valeur de retour d'une procédure est par convention le contenu du registre EAX. pour passer les paramètres à une procédure on push les valeurs dans la pile puis on appelle la proc qui va dépiler ces valeurs pour les traiter et les re-empiler avant le ret. on peut faire différemment bien sur et dire dans les specs comment la proc fonctionne puisque en assembleur un call est un jump/goto spécial avec un ret/return à l'adresse qui a lancé le call. les fonctions comme en C et les méthodes objet ne sont que des artéfact de conception que le compilateur gère et optimise selon les situations. par exemple on peut coder en asm de telle manière à dire que eax, ebx sont deux valeurs et que le résultat du calcul sera dans ecx. cette valeur de retour est généralement utilisé en tant que flag, valeur de résultat de calcul ou pointeur, la pile étant utilisée pour les choses plus complexes mais il faut savoir que les push/pop utilisés pas seulement pour sauver l'état des registres sont gourmands en ressources cpu et qu'utiliser les registres est beaucoup plus rapide.

    http://asm.developpez.com/cours

    Exemple 1 : le compilateur VS.NET C# place les deux paramètres dans les registres ECX et EDX, puis la valeur de retour de la proc dans EAX (RSP est le registre d'adresse du heap ou tas, RBP étant de la pile).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    {
      int v = Function(1, 2);
    
        mov         edx,2  // param2 = 2
        mov         ecx,1  // param1 = 1
        call        FFFFFFFFFFEC93C0  // call method entry point
        mov         dword ptr [rbp+18h],eax  // v = method result
    }
    
    static public int Function(int a, int b)
    {
      return a + b;
      00000000  mov         dword ptr [rsp+10h],edx 
      00000004  mov         dword ptr [rsp+8],ecx 
      00000008  sub         rsp,28h 
      0000000c  nop              
      0000000d  mov         rax,7FF001F0A18h 
      00000017  mov         eax,dword ptr [rax] 
      00000019  test        eax,eax 
      0000001b  je          0000000000000022 
      0000001d  call        FFFFFFFFF53A8740 
      00000022  mov         ecx,dword ptr [rsp+38h] 
      00000026  mov         eax,dword ptr [rsp+30h] 
      0000002a  add         eax,ecx 
      0000002c  jmp         000000000000002E 
      0000002e  add         rsp,28h 
      00000032  rep ret
    }
    Exemple 2 : avec params out et retour d'une référence, on constate qu'en IL x64 les classiques push & pop sont remplacés par le compilateur par l'utilisation directe de RBP, RAX étant l'équivalent 64bits de EAX, nécessaire pour la référence.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    {
      int count;
      var v = Function(1, 2, out count);
        lea         r8,[rbp+8]  // définition de count
        mov         edx,2  // param2 = 2
        mov         ecx,1  // param1 = 1
        call        FFFFFFFFFFEC95E0  // call method entry point
        mov         qword ptr [rbp+20h],rax   // v = method result
        mov         eax,dword ptr [rbp+8]  // nouvelle valeur de count
    }
    
    static public List<int> Function(int a, int b, out int count)
    {
      var result = new List<int>();
      00000000  mov         qword ptr [rsp+18h],r8 
      00000005  mov         dword ptr [rsp+10h],edx 
      00000009  mov         dword ptr [rsp+8],ecx 
      0000000d  sub         rsp,38h 
      00000011  mov         qword ptr [rsp+20h],0 
      0000001a  mov         rax,7FF001E0A18h 
      00000024  mov         eax,dword ptr [rax] 
      00000026  test        eax,eax 
      00000028  je          000000000000002F 
      0000002a  call        FFFFFFFFF53AE090 
      0000002f  mov         rcx,7FEF44846F0h 
      00000039  call        FFFFFFFFF514CEF0 
      0000003e  mov         qword ptr [rsp+28h],rax 
      00000043  mov         rcx,qword ptr [rsp+28h] 
      00000048  call        FFFFFFFFF41F0840 
      0000004d  mov         r11,qword ptr [rsp+28h] 
      00000052  mov         qword ptr [rsp+20h],r11 
    
      count = 2;
      00000057  mov         rax,qword ptr [rsp+50h] 
      0000005c  mov         dword ptr [rax],2 
    
      result.Add(a + b);
      00000062  mov         eax,dword ptr [rsp+48h] 
      00000066  mov         edx,dword ptr [rsp+40h] 
      0000006a  add         edx,eax 
      0000006c  mov         rax,qword ptr [rsp+20h] 
      00000071  cmp         byte ptr [rax],0 
      00000074  mov         rcx,qword ptr [rsp+20h] 
      00000079  call        FFFFFFFFF41F4C00 
    
      result.Add(a - b);
      0000007e  mov         r11d,dword ptr [rsp+48h] 
      00000083  mov         edx,dword ptr [rsp+40h] 
      00000087  sub         edx,r11d 
      0000008a  mov         rax,qword ptr [rsp+20h] 
      0000008f  cmp         byte ptr [rax],0 
      00000092  mov         rcx,qword ptr [rsp+20h] 
      00000097  call        FFFFFFFFF41F4C00 
    
      return result;
      0000009c  mov         rax,qword ptr [rsp+20h] 
      000000a1  jmp         00000000000000A3 
      000000a3  add         rsp,38h 
      000000a7  rep ret

  7. #27
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Octobre 2010
    Messages
    19
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2010
    Messages : 19
    Points : 31
    Points
    31
    Par défaut
    Je pense que c'est historique. Les compilateurs utilisent le registre accumulateur pour sauvegarder la valeur de retour. Et comme y avait pas beaucoup de registres sur les processeurs, ça marchait comme ça.

  8. #28
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par tomlev Voir le message
    J'ai pas compris... sum et count ne sont pas des variables de la méthode, c'est juste les noms des membres du tuple que la méthode doit renvoyer
    ce que je disait était de la réflexion pour finalement penser que l'idée développée dans d'autres langages était intéressante au même titre que de pouvoir utiliser une variable mot clé "résult" par défaut comme en pascal et qui manque au c#, sauf que là, l'idée est d'autoriser le nommage et la pluralité tel que :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    (int count, string str) Function(int a, int b)
    {
      count = 2;
      str = a.ToString() + " - " + b.ToString();
    }
    et pour l'utilisation on aurait en effet :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    (var c, var s) = Function(1, 2);
    au compilateur après de gérer.

  9. #29
    Membre éclairé
    Homme Profil pro
    nop
    Inscrit en
    Mars 2015
    Messages
    436
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Somme (Picardie)

    Informations professionnelles :
    Activité : nop
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2015
    Messages : 436
    Points : 658
    Points
    658
    Par défaut
    je vois bcp de commentaires avec du code dedans...
    Contourner cette limite est pourtant très simple depuis plus de 20ans : sérialiser la valeur retournée ! Avec PHP c'est automatique, sinon avec les autres langages on peut le faire manuellement.
    si j'ai besoin de retourner 5 valeurs de types distincts par exemple :
    -465
    -True
    -2012-12-15
    -Michael
    -12.45

    il suffit de les sérialiser manuellement, c'est-à-dire les concaténer en chaines de caractère :
    "465#True#2012-12-15#Michael#12.45"

    et de retourner cette simple chaîne. Biensûr, à la sortie n'oubliez pas d'indiquer dans la doc, comment exploiter le résultat (parcéliser..ec).
    Et si la chaîne contient des données sensibles, cette sérialisation est le moment idéal pour l'encoder/encrypter...

    Ce principe de sérialisation est disponible dans tous les langages, donc je ne vois pas pourquoi on en fait tout un article et du tapage comme ça.

  10. #30
    Expert éminent
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2007
    Messages
    2 161
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Août 2007
    Messages : 2 161
    Points : 7 952
    Points
    7 952
    Par défaut
    La réponse à cette question est très bien donnée dans le topic.

    Conceptuellement et mathématiquement, le résultat d'une fonction est lié à son contexte.
    Contexte défini par les paramètres.

    Si jamais le résultat est un objet complexe, il forme un tout cohérent et logique.
    Il est donc normal que toutes les données qu'il contient soient liées entre elles.

  11. #31
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par emotionengine Voir le message
    Je pense que c'est historique. Les compilateurs utilisent le registre accumulateur pour sauvegarder la valeur de retour. Et comme y avait pas beaucoup de registres sur les processeurs, ça marchait comme ça.
    historique à chaque instant qui passe aujourd'hui. par exemple, le compilateur c# va générer du code IL puis le CLR va générer depuis ce code IL sur une machine Intel du code x86 pour x32 ou x64 selon AnyCPU, CPU32 ou CPU64. l'assembleur des 8086/8088 d'il y a 20 ans a peu changé depuis et c'est le même sur les Core i7 et même sur toutes les puces en fait, à peu de choses près, on nomme les opcodes selon l'envie et les registres selon l'architecture eax ou rax, mais une pomme reste une pomme, quelle que soit la variété, et on appelle ça des microprocesseurs. pour le futur on parle de processeur quantique ou à adn par exemple.

    https://fr.wikipedia.org/wiki/Assembleur

    alors bien sur, plus on ajoute de couche d'abstraction, plus la conceptualisation du code et des données se complexifie. lorsqu'on maitrise une couche, on en ajoute une. la problématique étant les performances et c'est le facteur critique qui freine l'évolution. hier, les objets étaient super lents sur un i486, aujourd'hui on peut faire du polymorphisme générique que le c# gère même pas

    en effet, il est impossible d'écrire ça en c# :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    class Class<T>
    {
      T Value;
      public string DoSomething()
      {
        MessageBox.Show(Value.ToString());
      }
    }
    
    {
      var list = new List<Class<>>();
      list.Add(new Class<int>(1));
      list.Add(new Class<MachinTrucBidule>(quelquechose));
      list.Add(new Class<string>("test"));
      foreach (var item in list) 
      {
        if (item is Class<MachinTrucBidule>) continue;
        item.DoSomething();
      }
    }
    faut utiliser une interface mais c'est moche...

    c'est pareil avec le mot clé singleton qui manque au c# au même titre que class ou interface... et aucune implémentation de moins d'une centaines de ligne n'est correcte pour offrir abstraction et singletonnerie.

  12. #32
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Citation Envoyé par MichaelREMY Voir le message
    je vois bcp de commentaires avec du code dedans...
    Contourner cette limite est pourtant très simple depuis plus de 20ans : sérialiser la valeur retournée ! Avec PHP c'est automatique, sinon avec les autres langages on peut le faire manuellement.
    si j'ai besoin de retourner 5 valeurs de types distincts par exemple :
    -465
    -True
    -2012-12-15
    -Michael
    -12.45

    il suffit de les sérialiser manuellement, c'est-à-dire les concaténer en chaines de caractère :
    "465#True#2012-12-15#Michael#12.45"

    et de retourner cette simple chaîne. Biensûr, à la sortie n'oubliez pas d'indiquer dans la doc, comment exploiter le résultat (parcéliser..ec).
    Et si la chaîne contient des données sensibles, cette sérialisation est le moment idéal pour l'encoder/encrypter...

    Ce principe de sérialisation est disponible dans tous les langages, donc je ne vois pas pourquoi on en fait tout un article et du tapage comme ça.
    Parce que c'est horriblement peu efficace de devoir écrire et re-parser chaque valeur à chaque étape de l'appel? Surtout des dates?

    Sans compter les soucis liés au fait de devoir retourner plusieurs chaînes (au minimum, il faut échapper le caractère de séparation, ainsi que le caractère d'échappement puisqu'ici le séparateur ne peut pas être simplement échappé en le doublant)
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  13. #33
    Expert éminent
    Avatar de _skip
    Homme Profil pro
    Développeur d'applications
    Inscrit en
    Novembre 2005
    Messages
    2 898
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur d'applications
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Novembre 2005
    Messages : 2 898
    Points : 7 752
    Points
    7 752
    Par défaut
    Citation Envoyé par MichaelREMY Voir le message
    il suffit de les sérialiser manuellement, c'est-à-dire les concaténer en chaines de caractère :
    "465#True#2012-12-15#Michael#12.45"

    et de retourner cette simple chaîne. Biensûr, à la sortie n'oubliez pas d'indiquer dans la doc, comment exploiter le résultat (parcéliser..ec).
    Et si la chaîne contient des données sensibles, cette sérialisation est le moment idéal pour l'encoder/encrypter...

    Ce principe de sérialisation est disponible dans tous les langages, donc je ne vois pas pourquoi on en fait tout un article et du tapage comme ça.
    Peut être parce que c'est lent, fastidieux, casse-gueule et difficilement évolutif? Ou encore que ça pourrait reposer au final sur des mécanismes incertains pouvant conduire à la perte de précision (formatage des dates et des nombres à virgules flottantes).
    Il serait 100 fois préférable de créer une classe de données que de faire ce que tu proposes, je suis pas sûr que ce soit plus lourd qu'écrire un code de parsing à chaque appel. Puisque PHP est semble-t-il ton langage de référence, à la limite tu aurais pu proposer de retourner un tableau associatif. Mais j'oserai même pas utiliser ta solution même sur un petit projet de prototypage personnel et si un membre de mon équipe a recours à cela, c'est la pelle à neige, direct .

    Citation Envoyé par Sevyc64
    Mais effectivement, ça aurait une grande utilité que personne n'a mentionné.
    Une fonction, en réalité, a besoin de retourner 2 résultats, et pas un seul
    - le résultat du calcul, certes
    - mais aussi et surtout le résultat de l’exécution du calcul, s'il s'est bien effectué, avec ou sans erreur, etc.
    Sur ce point, on a tous pris l'habitude de donner des valeurs particulières au résultat (du calcul) pour faire passer l'information sur l’exécution de ce même calcul. Parce qu'on a pas vraiment le choix, mais conceptuellement c'est quand même une belle aberration. Qui pourrait être résolue pas les fonctions multi-retour
    C'est vrai qu'on voyait des contournements assez moches pour ce problème, genre on faisait un appel de fonction, il retournait 0, puis on appelait getLastError() pour connaître les détails. Ce qui laissait supposer qu'une bonne grosse variable globale (ou threadlocale) dégueulasse était populée et réinitailisée quelque part, presque impensable de nos jours mais c'était l'un des premiers soucis qu'on avait avec cette "limitation" d'un seule valeure retournée. Certains langages de nos jours proposent un type union qui permet de retourner de façon sécurisée une valeur d'un type ou l'autre, c'est pas trop mal foutu. Il me semble que Ceylon le fait, et que c'est proposé d'être reprit par typescript.

    Pour ce qui est de retourner plusieurs valeurs, ou d'utiliser un sucre syntaxique pour ne pas avoir de déclarer explicitement une classe spécifique ou une structure, ça peut être bien utile. Je ne trouve pas que ce soit plus moche que les tuples, après pour la sémantique qui se perd en cours de route si on en abuse, le problème se pose aussi avec les fonctions qui prennent trop d'arguments dans les langages qui ne proposent pas les "named arguments".

  14. #34
    Expert confirmé Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Points : 5 485
    Points
    5 485
    Par défaut
    Citation Envoyé par MichaelREMY Voir le message
    je vois bcp de commentaires avec du code dedans...
    Contourner cette limite est pourtant très simple depuis plus de 20ans : sérialiser la valeur retournée ! Avec PHP c'est automatique, sinon avec les autres langages on peut le faire manuellement.
    si j'ai besoin de retourner 5 valeurs de types distincts par exemple :
    -465
    -True
    -2012-12-15
    -Michael
    -12.45

    il suffit de les sérialiser manuellement, c'est-à-dire les concaténer en chaines de caractère :
    "465#True#2012-12-15#Michael#12.45"
    Vade retro satanas !

    Les autres se sont déjà chargés d'expliquer plusieurs des nombreuses raisons pour lesquelles tout ça est une très mauvaise idée.

  15. #35
    Expert éminent sénior

    Homme Profil pro
    pdg
    Inscrit en
    Juin 2003
    Messages
    5 750
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : pdg

    Informations forums :
    Inscription : Juin 2003
    Messages : 5 750
    Points : 10 670
    Points
    10 670
    Billets dans le blog
    3
    Par défaut
    Citation Envoyé par Gugelhupf Voir le message
    De plus un passage par copie de référence (Java) ou référence (C++) suffit largement pour avoir une fonction qui modifie plusieurs variables.
    Même en C++ la tendance est d'éviter les paramètres OUT (passage par référence) et privilégier un style plus fonctionnel : paramètres IN, résultat en retour de fonction (par valeur et non pointeur), pas d'effet de bord / état (var globale, singleton, ...). La move semantic facilite la généralisation de ce style tout en en minimisant le prix (à savoir la copie de l'objet retourné).

    Ainsi en C++11, on a std::tuple et std::tie par exemple:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::tie(position, already_existed) = myMap.insert(...);
    Les tuples sont moins sympas à utiliser que des struct/class effectivement... en général... car des fois c'est sympa, par exemple récupérer le résultat d'une requête SQL sans se cogner une usine à gaz qui génère plein de classes dans tous les sens :
    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    // renvoie un std::tuple<string, string, time>
    auto result = execQuery<string, string, int>("SELECT nom, prenom, age FROM blablabla");
     
    // variante: nombre variable de paramètres d'entrée et de retour
    string nom, prenom;
    tie(nom, prenom) = execQuery<string, string>("SELECT nom, prenom FROM TablePerson WHERE id=%1", personId);

    Toujours dans l'optique de privilégier les valeurs de retour aux paramètres OUT, optional<> rend le code bien plus sexy:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    int width = settings["windowWidth"].value_or(400);
    vs:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    int width;
    if (!settings.get("windowWidth", width) {
        width = 400;
    }

  16. #36
    Membre habitué
    Homme Profil pro
    Electronicien
    Inscrit en
    Novembre 2012
    Messages
    35
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loiret (Centre)

    Informations professionnelles :
    Activité : Electronicien
    Secteur : Transports

    Informations forums :
    Inscription : Novembre 2012
    Messages : 35
    Points : 131
    Points
    131
    Par défaut Pas une seule valeur, une seule variable !
    Sauf erreur de ma part, la définition mathématique d'une fonction est du type :
    Y=f(X) ou Y=f(X1,X2, ..., Xn)
    Elle ne retourne qu'une seule variable.
    Après, la variable peut-être complexe.
    Une fonction qui retourne une couleur retournera probablement un triplet (C,M,Y) ou (R,G,B) donc pas une seule valeur mais trois.
    Idem pour une vitesse dans un espace tridimensionnel, V=(Vx,Vy,Vz) ou V=(v,θ,Ψ)
    Une fonction peut aussi retourner les coordonnées d'une personne.
    Bref, je ne vois pas où est le problème ?

  17. #37
    Membre extrêmement actif Avatar de ddoumeche
    Homme Profil pro
    Ingénieur recherche et développement
    Inscrit en
    Octobre 2007
    Messages
    1 676
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Singapour

    Informations professionnelles :
    Activité : Ingénieur recherche et développement

    Informations forums :
    Inscription : Octobre 2007
    Messages : 1 676
    Points : 2 009
    Points
    2 009
    Par défaut
    A partir du moment où une fonction est suffisamment "complexe" pour retourner plusieurs valeur, elle est mature pour retourner une structure.
    Idem pour les paramètres, ils sont avantageusement remplacés par une structure complexe dès que leur nombre dépasse les 3 ou 4.

    L'avantage de cette approche est qu'on gère beaucoup mieux la compatibilité ascendante, avec un nombre de champs arbitraire et qui n'obligera pas à se prendre la tête dans le corps de la fonction pour prendre en compte tous les scénarios possibles.

    Oui les structures n'existent pas en Java et sont remplacées par des classes, lesquelles sont des instances donc ont un coût.
    Par expérience, dans 99% des cas, le gain sera supérieur à ce coût.. D'ailleurs quel est-t'il ?
    La joie de l'âme est dans la planification -- Louis Hubert Liautey

  18. #38
    Membre à l'essai
    Homme Profil pro
    Développeur Web
    Inscrit en
    Octobre 2013
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Octobre 2013
    Messages : 2
    Points : 11
    Points
    11
    Par défaut Golang
    Le langage Golang de Google gère lui aussi nativement les fonctions à retours multiples et c'est ma foi bien pratique parfois.

  19. #39
    Membre averti
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    187
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 187
    Points : 434
    Points
    434
    Par défaut
    J'aurais dit que ça vient des maths. Une fonction peut avoir N paramètre, mais n'est définie que dans un ensemble (= l'ensemble des valeurs de retour = le type de valeur de retour).

    Bien sûr une fonction en math peut retourner un nombre imaginaire, une coordonnée etc. mais l'unique valeur retournée peut alors être une structure / un objet.

  20. #40
    Membre éclairé
    Profil pro
    Inscrit en
    Avril 2008
    Messages
    467
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2008
    Messages : 467
    Points : 681
    Points
    681
    Par défaut
    Citation Envoyé par tomlev Voir le message
    Oui mais ni les paramètres out, ni la classe Tuple<> ne sont très pratiques à utiliser... Du coup il est envisagé d'ajouter le support de "vrais" tuples à C# 7:

    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    public (int sum, int count) Tally(IEnumerable<int> values) { ... }
     
    ...
     
    (var sum, var count) = Tally(myValues);
    Console.WriteLine($"Sum: {sum}, count: {count}");
    + 1000 ! J'attends cette fonctionnalité depuis des années. Bien plus pratique et agréable que les "Tuple<>" et on verra p'être enfin la fin des paramètres "out" déprécié - on peut rêver

    Par contre il se cache aussi deux notion distincts derrière les retours-multiples. Celle qu'on vient de voir et celle avec sorties distinctes comme par exemple un résultat et un code erreur.

    L'utilisation d'un tuple ne semble pas mieux adapter dans ce cas. Car quand on a un résultat on n'a pas besoin du code erreur et inversement. C'est donc deux sorties différentes qu'il faut.

    Je remplacerais bien ça
    if(Int.TryParse(str, out int result))
    {
    // c'est bon
    }
    else
    {
    // c'est pas bon
    }
    par
    try(var result = Int.Parse(str))
    {
    // c'est bon
    }
    else
    {
    // c'est pas bon
    }
    Ca serait un "try" sans la complexité de gestion des exceptions qui transformerait une méthode contenant un "throw" en une nouvelle avec deux sorties.

    Pensez vous qu'il y ait besoin de plus de deux sorties ? J'en trouve pas de concrets là.

    Sinon on peut toujours dès maintenant utiliser l'écriture fonctionnelle ; "Int.Parse(string str, Action<int> sortie_standard, Action sortie_erreur)"... mais c'est pas très bÔ à utiliser avec les crochets et le parenthèses un peu partout :o

Discussions similaires

  1. [AC-2003] Une formule pour calculer les in et out d une table de pointage
    Par taz devil dans le forum Requêtes et SQL.
    Réponses: 10
    Dernier message: 29/06/2015, 12h46
  2. Réponses: 10
    Dernier message: 10/02/2010, 08h49
  3. Réponses: 6
    Dernier message: 06/05/2009, 15h35
  4. [Excel] fonction SOMME.SI avec une cellule pour critère
    Par repié dans le forum Macros et VBA Excel
    Réponses: 3
    Dernier message: 15/03/2006, 17h39

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo