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

Affichage des résultats du sondage: Pourquoi C et C++ auraient-ils encore de nombreuses années devant eux ?

Votants
75. Vous ne pouvez pas participer à ce sondage.
  • C et C++ permettent d'avoir plus de contrôle sur le matériel

    41 54,67%
  • C et C++ vous permettent d'écrire du code très efficace

    38 50,67%
  • Les langages C et C++ sont portables

    35 46,67%
  • C et C++ sont des langages qui évoluent

    19 25,33%
  • C et C++ sont largement utilisés

    48 64,00%
  • C++ a peut-être de l'avenir, mais je doute que ça soit le cas de C

    8 10,67%
  • C a peut-être de l'avenir, mais je doute que ça soit le cas de C++

    3 4,00%
  • Je pense qu'ils n'ont plus beaucoup d'années devant eux

    6 8,00%
  • Autre (à préciser)

    3 4,00%
  • Pas d'avis

    3 4,00%
Sondage à choix multiple
Langages de programmation Discussion :

Pourquoi les langages C et C++ auraient-ils encore de nombreuses années devant eux ?


Sujet :

Langages de programmation

  1. #221
    Membre régulier
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2017
    Messages
    36
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2017
    Messages : 36
    Points : 95
    Points
    95
    Par défaut
    Citation Envoyé par thierryc Voir le message
    - "prenez Fortran, c'est mieux que le C++ pour le calcul scientifique" : c'est d'autant plus vrai que ça libère du temps de cerveau de scientifique de ne pas avoir à pensez aux astuces du C++, mais "simplement" de penser à leur problème scientifique à résoudre. En plus, les dernières versions de Fortran sont très intéressantes, avec notamment, l'introduction du concept de matrice dans les primitives du langage. Également, Fortran n'a jamais été aussi facile à interfacer, c'est très économique et puissant de réaliser un cœur de calcul en Fortran et d'y ajouter la décoration
    Thierryc
    Ça libère du temps de cerveau... qui sera largement repris un peu plus tard car le problème n'aura pas été décomposé en composants et concepts indépendants, et le code aura été dupliqué à l'infini avec des changements infimes. Le Fortran ne possède pas de mécanismes qui permettent de créer des composants car il n'y a pas de support (ou très limité) pour la programmation générique.

    Je pense qu'il est dangereux de parler des problèmes qui se posent dans l'informatique scientifique comme annexes aux autres problèmes. C'est une attitude courante dans la communauté qui ne voit pas l'aspect pervasif de la programmation et qui refuse de considérer la programmation comme une partie intégrante de la simulation numérique, à laquelle il faut réfléchir, intéressante et complexe. Elle est plus considérée comme une corvée à faire à l'arrache pour pouvoir lancer une simulation et vérifier une méthode numérique.

    Quant à l'interfaçage du Fortran avec un autre langage, c'est comme pour tous les langages : tant qu'on interface des types primitifs et des tableaux de types primitifs, il n'y a aucun souci. Mais aller au delà, c'est extrêmement laid. J'ai vu des exemples Fortran/C++, C++/Python, c'est systématique.

  2. #222
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par _Bérenger_ Voir le message
    Quant à l'interfaçage du Fortran avec un autre langage, c'est comme pour tous les langages : tant qu'on interface des types primitifs et des tableaux de types primitifs, il n'y a aucun souci. Mais aller au delà, c'est extrêmement laid. J'ai vu des exemples Fortran/C++, C++/Python, c'est systématique.
    Je ne suis pas vraiment d'accord avec cette partie. C'était peut-être le cas il y a 15 ans mais aujourd'hui il y a de très bons outils.
    Pour le C, énormément de langages proposent des interfaces "officielles".
    Pour c++/python, tu peux utiliser boost python ou pybind11. Ca marche très bien et ça s'intégre dans les systèmes de build genre cmake ou setuptools. Tu peux même utiliser des objets eigen dans ta lib c++ et les récupérer sous forme d'objets numpy dans ton code python. D'ailleurs il y a de plus en plus de frameworks c++/python destinés au calcul scientifique.

  3. #223
    Expert confirmé Avatar de AoCannaille
    Inscrit en
    Juin 2009
    Messages
    1 413
    Détails du profil
    Informations forums :
    Inscription : Juin 2009
    Messages : 1 413
    Points : 4 734
    Points
    4 734
    Par défaut
    Citation Envoyé par _Bérenger_ Voir le message
    Quant à l'interfaçage du Fortran avec un autre langage, c'est comme pour tous les langages : tant qu'on interface des types primitifs et des tableaux de types primitifs, il n'y a aucun souci. Mais aller au delà, c'est extrêmement laid. J'ai vu des exemples Fortran/C++, C++/Python, c'est systématique.
    ça dépend des langages quand même. Par exemple, grâce à Protocol buffer, on peut interfacer proprement les langages suivants : C++, C#, Go, Java et python.
    On commence par définir les "messages", qui peuvent contenir des types simple ou des combinaisons de types simple, et ce à n'importe quelle profondeur (Un objet A contenant une string, un double, une liste d'objet B qui lui est composé de 2 listes de messages C et D etc. etc.). Protobuf compile l'IDL, génére le code utilitaire associé et c'est parti pour la programmation objet des deux cotés du tuyau.

  4. #224
    Membre régulier
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2017
    Messages
    36
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2017
    Messages : 36
    Points : 95
    Points
    95
    Par défaut
    Citation Envoyé par SimonDecoline Voir le message
    Je ne suis pas vraiment d'accord avec cette partie. C'était peut-être le cas il y a 15 ans mais aujourd'hui il y a de très bons outils.
    Pour le C, énormément de langages proposent des interfaces "officielles".
    Pour c++/python, tu peux utiliser boost python ou pybind11. Ca marche très bien et ça s'intégre dans les systèmes de build genre cmake ou setuptools. Tu peux même utiliser des objets eigen dans ta lib c++ et les récupérer sous forme d'objets numpy dans ton code python.
    Je n'ai pas essayé avec boost ou pybind11, mais j'ai quand même des doutes. Je l'ai fait avec Cython. Ce n'est peut être pas si terrible, mais ça reste lourd.

    Citation Envoyé par SimonDecoline Voir le message
    D'ailleurs il y a de plus en plus de frameworks c++/python destinés au calcul scientifique.
    Je partage le constat de popularité. Je ne suis pas convaincu de l'interfaçage avec Python (ou un n'importe quel couple langage compilé / interprété). Je ne vois pas vraiment l'intérêt. Ou plutôt je pense qu'on surestime les avantages (des non-experts peuvent interagir avec le framework de manière plus souple) par rapport à l'investissement (un outil qui transfère les données d'un langage à l'autre).


    Et là on parle de l'interfaçage haut niveau. Quand il y a besoin d'échanger des données entre des structures dans le cœur du code, je pense honnêtement que c'est impossible entre deux langages avec à la fois une API potable et pas de pertes de performances. En pratique, ça se fait à partir de fonctions qui se conforment à l'API C, c'est très contraignant.

  5. #225
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par _Bérenger_ Voir le message
    Je n'ai pas essayé avec boost ou pybind11, mais j'ai quand même des doutes. Je l'ai fait avec Cython. Ce n'est peut être pas si terrible, mais ça reste lourd.
    Bah essaie pybind11, c'est assez impressionnant.

    Citation Envoyé par _Bérenger_ Voir le message
    Je partage le constat de popularité. Je ne suis pas convaincu de l'interfaçage avec Python (ou un n'importe quel couple langage compilé / interprété). Je ne vois pas vraiment l'intérêt. Ou plutôt je pense qu'on surestime les avantages (des non-experts peuvent interagir avec le framework de manière plus souple) par rapport à l'investissement (un outil qui transfère les données d'un langage à l'autre).
    L'intérêt c'est qu'il n'y a pas de transfert de données justement. Les grosses données et gros calculs sont faits dans le code C++. Le code python est juste un raccourci de syntaxe pour manipuler les classes et fonctions C++ plus simplement.

    Citation Envoyé par _Bérenger_ Voir le message
    Et là on parle de l'interfaçage haut niveau. Quand il y a besoin d'échanger des données entre des structures dans le cœur du code, je pense honnêtement que c'est impossible entre deux langages avec à la fois une API potable et pas de pertes de performances. En pratique, ça se fait à partir de fonctions qui se conforment à l'API C, c'est très contraignant.
    Pas forcément (cf l'interface eigen/numpy de pybind11). Et généralement, le but est justement de garder les structures et algos couteux dans le code c++.

  6. #226
    Membre régulier
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2017
    Messages
    36
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2017
    Messages : 36
    Points : 95
    Points
    95
    Par défaut
    Citation Envoyé par SimonDecoline Voir le message
    L'intérêt c'est qu'il n'y a pas de transfert de données justement. Les grosses données et gros calculs sont faits dans le code C++. Le code python est juste un raccourci de syntaxe pour manipuler les classes et fonctions C++ plus simplement.
    Je me suis mal exprimé. Je voulais parler du transfert/traduction des classes et fonctions C++ vers le python.
    Par exemple, en Cython, si tu veux réutiliser une structure/classe C++ côté Python, tu dois déclarer tous les membres/méthodes en Cython (rien ne l'extrait pour toi du .h). Et par exemple, tu ne peux pas utiliser de classe templaté par un entier (tu dois déclarer une classe comme instanciation d'une classe template). Pourquoi ? car ça n'a pas été codé dans le générateur Cython (on ne peut pas leur en vouloir, mais on doit contourner). Dans l'autre sens (C++ appelle Python) je ne sais même pas si c'est possible avec Cython. En bref : il manque des choses qui font que l'interfaçage est désagréable car on butte constamment sur ce genre de problème "à la con"


    Citation Envoyé par SimonDecoline Voir le message
    Et généralement, le but est justement de garder les structures et algos couteux dans le code c++.
    Oui quand tu interfaces avec Python. Mais pas quand tu interfaces avec Fortran.

  7. #227
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 469
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 469
    Points : 6 102
    Points
    6 102
    Par défaut
    Thierryc, j'ai une question technique sur Ada.

    On m'a dit que Ada avait un typage extrêmement fort et que, quand on définissait les bornes d'un type entier, Ada vérifiait directement à la compilation si on sortait des bornes.
    Mais est-ce qu'il permet de le faire avec une syntaxe facile à lire ? (Je ne sous-entends pas que ce n'est pas le cas, je pose seulement la question.)

    Pour illustrer mon propos, je vais prendre un exemple de code en C++.
    Un jour, j'avais écrit en C++ un code qui convertissait un nombre en lettres en français. Par exemple, si on lui donnait 42 en entrée, il retournait "quarante-deux".
    Je ne vais pas recopier ici le code en entier, mais voici le code d'une des sous-fonctions :
    Code c++ : 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
     
    // EDIT 22h05 : variable "commeAuQuebec " renommée en "septanteHuitanteNonante" suite à la remarque de jlliagre.
     
    std::string chaineNombreEntre1Et99(unsigned nombre, bool adjNumOrdinal, bool septanteHuitanteNonante)
    {
        assert(nombre >= 1);
        assert(nombre <= 99);
        std::string resultat{};
        if(nombre <= 19) {
            resultat = chaineNombreEntre1Et19(nombre);
        } else if(nombre <= 69 || septanteHuitanteNonante) {
            unsigned const dizaines = nombre / 10;
            unsigned const unites   = nombre % 10;
            resultat = motDe10FoisFacteurEntre1Et9(dizaines);
            if(unites >= 1) {
                if(unites == 1) {
                    resultat += u8" et un";
                } else {
                    resultat += u8'-';
                    resultat += motDeChiffre(unites);
                }
            }
        } else if(nombre <= 79) {
            resultat = motDe10FoisFacteurEntre1Et9(6);
            resultat += (nombre == 71) ? u8" et " : u8"-";
            resultat += chaineNombreEntre1Et19(nombre - 60);
        } else {
            resultat = u8"quatre-vingt";
            if(nombre == 80) {
                if(!adjNumOrdinal)
                    resultat += u8's';
            } else {
                resultat += u8'-';
                resultat += chaineNombreEntre1Et19(nombre - 80);
            }
        }
        assert(!resultat.empty());
        return resultat;
    }

    Dans ce code, les bornes des entiers sont vérifiées à l'exécution, en Debug.
    Mais j'aurais préféré que ces vérifications soient faites directement à la compilation.
    Récemment, je me suis demandé à quoi aurait ressemblé ce code en C++ si je l'avais écrit de telle sorte que les bornes des entiers soient vérifiées à la compilation.

    Alors, j'ai introduit les templates suivants :
    Code c++ : 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
     
    template<unsigned Min, unsigned Max>
    struct EntierBorne;
     
    template<unsigned N, unsigned Min, unsigned Max>
    EntierBorne<Min-N, Max-N> moins(EntierBorne<Min, Max> x);
     
    template<unsigned N, unsigned Min, unsigned Max>
    EntierBorne<Min/N, Max/N> divisionEuclidienne(EntierBorne<Min, Max> x);
     
    template<unsigned N, unsigned Min, unsigned Max>
    EntierBorne<0, N-1> modulo(EntierBorne<Min, Max> x);
     
    //! Si x < N, alors exécuter la routine en 2e paramètre, sinon celle en 3e.
    template<unsigned N, unsigned Min, unsigned Max, class CallableX, class CallableY>
    decltype(auto) ifLessThanThenElse(EntierBorne<Min, Max> x, CallableX&& whenLessThanN, CallableY&& otherwise);
     
    //! Si x < N || cond, alors exécuter la routine en 3e paramètre, sinon celle en 4e.
    template<unsigned N, unsigned Min, unsigned Max, class CallableX, class CallableY>
    decltype(auto) ifLessThanOrThenElse(EntierBorne<Min, Max> x, bool cond, CallableX&& whenCondOrLessThanN, CallableY&& otherwise);

    Et j'ai réécrit le code de chaineNombreEntre1Et99 ainsi :
    Code c++ : 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
    // EDIT 22h05 : variable "commeAuQuebec " renommée en "septanteHuitanteNonante" suite à la remarque de jlliagre.
     
    std::string chaineNombreEntre1Et99(EntierBorne<1, 99> nombre, bool adjNumOrdinal, bool septanteHuitanteNonante)
    {
        return ifLessThanThenElse<20>(
            nombre,
            [&](EntierBorne<1, 19> nombre) {
                return chaineNombreEntre1Et19(nombre);
            },
            [&](EntierBorne<20, 99> nombre) {
                return ifLessThanOrThenElse<70>(
                    nombre,
                    septanteHuitanteNonante,
                    [&](EntierBorne<20, 99> nombre) {
                        EntierBorne<2, 9> const dizaines = divisionEuclidienne<10>(nombre);
                        EntierBorne<0, 9> const unites   = modulo<10>(nombre);
                        std::string resultat = motDe10FoisFacteurEntre1Et9(dizaines);
                        if(unites >= 1) {
                            if(unites == 1) {
                                resultat += u8" et un";
                            } else {
                                resultat += u8'-';
                                resultat += motDeChiffre(unites);
                            }
                        }
                        return resultat;
                    },
                    [&](EntierBorne<70, 99> nombre) {
                        return ifLessThanThenElse<80>(
                            nombre,
                            [&](EntierBorne<70, 79> nombre) {
                                std::string resultat = motDe10FoisFacteurEntre1Et9(6);
                                resultat += (nombre == 71) ? u8" et " : u8"-";
                                resultat += chaineNombreEntre1Et19(moins<60>(nombre));
                                return resultat;
                            },
                            [&](EntierBorne<80, 99> nombre) {
                                std::string resultat = u8"quatre-vingt";
                                ifLessThanThenElse<81>(
                                    nombre,
                                    [&](EntierBorne<80, 80>) {
                                        if(!adjNumOrdinal)
                                            resultat += u8's';
                                    },
                                    [&](EntierBorne<81, 99> nombre) {
                                        resultat += u8'-';
                                        resultat += chaineNombreEntre1Et19(moins<80>(nombre));
                                    }
                                );
                                return resultat;
                            }
                        );
                    }
                );
            }
        );
    }
    Toutes les bornes sont rigoureusement vérifiées à la compilation, mais le code est lourd.
    A cause de la lourdeur, ce n'est pas le genre de code que j'écrirais dans la vie de tous les jours, surtout en entreprise.

    Est-ce que la fonction ci-dessus pourrait être réécrite en Ada de telle sorte que toutes les bornes soient vérifiées à la compilation ?
    Si oui, à quoi ressemblerait le code ? Je pose cette question pour pouvoir comparer la lisibilité avec le code ci-dessus.

  8. #228
    Modérateur
    Avatar de jlliagre
    Homme Profil pro
    Ingénieur support avancé & développement
    Inscrit en
    Juin 2007
    Messages
    2 695
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur support avancé & développement
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 2 695
    Points : 7 882
    Points
    7 882
    Par défaut
    <hors-sujet>

    J'ai trouvé deux bugs

    Citation Envoyé par Pyramidev Voir le message
    //! \remark Si commeAuQuebec == true, alors on écrit "septante", "octante" et "nonante"
    //! au lieu de "soixante", "soixante-dix" et "quatre-vingt".
    Dans le commentaire, il aurait fallu écrire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    //!     au lieu de "soixante-dix", "quatre-vingts" et "quatre-vingt-dix"
    De plus, le test est faux:

    • Au Québec, on dit exactement comme en France : soixante-dix, quatre-vingts et quatre-vingt-dix
    • En Belgique, on dit : septante, quatre-vingts et nonante
    • En Suisse romande, on dit : septante, au choix quatre-vingts (plutôt vers Genève) ou huitante (plutôt ailleurs, à Lausanne par exemple) et nonante.
    • On ne dit octante nulle part...


    </hors-sujet>
    ɹǝsn *sıɹɐlos*

  9. #229
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 469
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 469
    Points : 6 102
    Points
    6 102
    Par défaut
    Citation Envoyé par jlliagre Voir le message
    • Au Québec, on dit exactement comme en France : soixante-dix, quatre-vingts et quatre-vingt-dix
    • En Belgique, on dit : septante, quatre-vingts et nonante
    • En Suisse romande, on dit : septante, au choix quatre-vingts (plutôt vers Genève) ou huitante (plutôt ailleurs, à Lausanne par exemple) et nonante.
    • On ne dit octante nulle part...
    Ah, zut.
    Du coup, il faudra que je trouve un autre nom pour mon booléen.
    Merci pour l'info.

  10. #230
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par Pyramidev Voir le message
    Ah, zut.
    Du coup, il faudra que je trouve un autre nom pour mon booléen.
    Merci pour l'info.
    Ce qui prouve bien que C++ est un langage cancéreux qui doit être euthanasié. Alors qu'Ada aurait corrigé le bug tout seul, fait le café et changé les pneus de la ferrari...

  11. #231
    Membre régulier
    Inscrit en
    Décembre 2004
    Messages
    123
    Détails du profil
    Informations forums :
    Inscription : Décembre 2004
    Messages : 123
    Points : 97
    Points
    97
    Par défaut
    Bonjour Pyramidev;

    Oui, les bornes sont vérifiées à la compilation et en Ada, et en Pascal.
    le principe est de déclarer un type entier spécifique. En Pascal, de mémoire, cela donne:
    Code pascal : Sélectionner tout - Visualiser dans une fenêtre à part
    type TMonTypeEntier = 1..99;

    et vu la guerre linguistique qui a suivi ton post ;-), tu peux également déclarer un autre type très utile:
    Code pascal : Sélectionner tout - Visualiser dans une fenêtre à part
    type TContexteLangue = (clParDefaut, clFrance, clQuebec, clBelgique, clSuisseRomande, clSenegal);
    (et plus si nécessaire)

    C'est une première réponse rapide. Je vais regarder ton code et essayer le reproduire en Pascal, puis en Ada pour te donner des points de comparaison. Laisse-moi 2, 3 jours, s'il te plaît.
    Bon week-end.
    Cordialement,
    Thierryc.

  12. #232
    Membre régulier
    Inscrit en
    Décembre 2004
    Messages
    123
    Détails du profil
    Informations forums :
    Inscription : Décembre 2004
    Messages : 123
    Points : 97
    Points
    97
    Par défaut
    Citation Envoyé par thierryc Voir le message
    Bonjour Pyramidev;
    C'est une première réponse rapide.
    Thierryc.
    Si j'ai bien compris ce que tu veux faire, tu trouveras ci-dessous le source de l'unité de conversion en Pascal. Cela compile en FPC, donc tu pourras tester librement de ton côté. J'ai rapidement fait la version procédurale, en imitant la structuration de ton propre code, avec une fenêtre de tests. Quand j'ai un peu plus de temps, je donnerai les versions orientées objet et composant du même algorithme. Ainsi que les tests unitaires.

    Code pascal : 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
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    unit UConversionNombreLettres;
    {$mode delphi}{$H+}
    // Traduction du code de Pyramidev en C++ pour convertir un nombre en toutes lettres
    // Trois approches sont possibles en Pascal : procédurale, objet et composant.
    // Celle-ci est la version procédurale.
    // (c) Thierryc 2018
     
    interface
     
    uses
      Classes, SysUtils;
     
    type TEntierBorneParle = 0..99;
    type TContexteLangue = (clParDefaut, clFrance, clQuebec, clBelgique, clSuisseRomande, clSuisseRomandeGeneve, clSenegal);
    type TNombreEnChaine = type string;
    type EConversioNombre = class(Exception);
     
    function ConvertirNombreEnChaine( unEntier: TEntierBorneParle; unContexte: TContexteLangue): TNombreEnChaine;
     
    implementation
     
    type TUnites = 0..9;
    type TUnitesComposees = 0..19;
    type TDizainesNormales = 2..6;
    type TDizaines = 0..9;
     
     
    //toutes les chaines déclarées dans un block "resourceString" sont modifiables dans l'éxecutable par un éditeur de ressources.
    resourceString
      //séparateurs de nombres
      rsEspaceNombre = ' ';
      rsTiretNombre  = '-';
      rsEt           = ' et ';
     
      //Unités:
      rsZero            = 'zéro';
      rsUn              = 'un';
      rsDeux            = 'deux';
      rsTrois           = 'trois';
      rsQuatre          = 'quatre';
      rsCinq            = 'cinq';
      rsSix             = 'six';
      rsSept            = 'sept';
      rsHuit            = 'huit';
      rsNeuf            = 'neuf';
      //Entre dix et dix-neuf
      rsDix             = 'dix';
      rsOnze            = 'onze';
      rsDouze           = 'douze';
      rsTreize          = 'treize';
      rsQuatorze        = 'quatorze';
      rsQuinze          = 'quinze';
      rsSeize           = 'seize';
      //dizaines
      rsVingt           = 'vingt';
      rsTrente          = 'trente';
      rsQuarante        = 'quarante';
      rsCinquante       = 'cinquante';
      rsSoixante        = 'soixante';
     
      //entiers par contexte..
      rs70ParDefault    = 'soixante-dix';
      rs70Belge         = 'septante';
     
      rs80ParDefault    = 'quatre-vingts';
      rs80SuisseRomande = 'huitante';
     
      rs90ParDefault    = 'quatre-vingt-dix';
      rs90Belge         = 'nonante';
     
      //messaqes d'erreur:
      eUniteNonComprise = 'unité non comprise';
      eDizaineNonComprise = 'dizaine non comprise';
      eNombrePasCompris = 'Nombre pas compris';
     
    procedure DecomposerNombre( unEntier : TEntierBorneParle; out lUnite: TUnites; out laDizaine: TDizaines);
    begin
      lUnite    := unEntier mod 10;
      laDizaine := unEntier div 10;
    end;
     
     
    function ConvertirNombreEntre00et10( unEntier: TUnites; unContexte: TContexteLangue = clParDefaut): TNombreEnChaine;
    begin
      case unEntier of
         0  : result := rsZero;
         1  : result := rsUn;
         2  : result := rsDeux;
         3  : result := rsTrois;
         4  : result := rsQuatre;
         5  : result := rsCinq;
         6  : result := rsSix;
         7  : result := rsSept;
         8  : result := rsHuit;
         9  : result := rsNeuf;
         10 : result := rsDix;
         else
           raise EConversioNombre.Create( eUniteNonComprise) ;
      end;
    end;
     
     
    function ConvertirDizaines( uneDizaine: TDizainesNormales; unContexte: TContexteLangue = clParDefaut): TNombreEnChaine;
    begin
      case uneDizaine of
         2 : result := rsVingt;
         3 : result := rsTrente;
         4 : result := rsQuarante;
         5 : result := rsCinquante;
         6 : result := rsSoixante;
         else
           raise EConversioNombre.Create( eDizaineNonComprise);
      end;
    end;
     
     
    function ComposerChainesDizainesEtUnites( laDizaine: TNombreEnChaine; lUnite: TNombreEnChaine) : TNombreEnChaine;
    begin
      if lUnite = rsZero then
        result := laDizaine
      else if ((lUnite=rsUn) or (lUnite=rsOnze)) and (laDizaine<>rs80ParDefault) then
        result := laDizaine + rsEt + lUnite
      else
        result := laDizaine + rsTiretNombre + lUnite;
    end;
     
     
    function ConvertirNombreEntre11et19( unEntier: TEntierBorneParle; unContexte: TContexteLangue): TNombreEnChaine;
    begin
      case unEntier of
         11 : result := rsOnze;
         12 : result := rsDouze;
         13 : result := rsTreize;
         14 : result := rsQuatorze;
         15 : result := rsQuinze;
         16 : result := rsSeize;
         17..19 : result := rsDix + rsTiretNombre + ConvertirNombreEntre00et10( unEntier mod 10);
         else
           raise EConversioNombre.Create(eNombrePasCompris);
      end;
    end;
     
     
    function ConvertirNombreEntre20et69( unEntier: TEntierBorneParle; unContexte: TContexteLangue): TNombreEnChaine;
    var
      laDizaine    : TDizainesNormales;
      lUnite       : TUnites;
      nbreDizaines : TNombreEnChaine;
      nbreUnites   : TNombreEnChaine;
    begin
      DecomposerNombre(unEntier, lUnite, laDizaine);
      nbreDizaines := ConvertirDizaines(laDizaine);
      nbreUnites   := ConvertirNombreEntre00et10(lUnite);
      result       := ComposerChainesDizainesEtUnites(nbreDizaines, nbreUnites);
    end;
     
     
    function ConvertirNombreEntre70et79( unEntier: TEntierBorneParle; unContexte: TContexteLangue): TNombreEnChaine;
    var
      lUnite       : TUnites;
      lUniteComp   : TUnitesComposees;
      nbreDizaines : TNombreEnChaine;
      nbreUnites   : TNombreEnChaine;
    begin
      case unContexte of
        clBelgique, clSuisseRomande, clSuisseRomandeGeneve :
        begin
          lUnite:= unEntier - 70;
          nbreDizaines  := rs70Belge;
          nbreUnites    := ConvertirNombreEntre00et10(lUnite);
          result := ComposerChainesDizainesEtUnites(nbreDizaines, nbreUnites);
        end
        else
        begin
          nbreDizaines := rsSoixante;
          lUniteComp   := unEntier - 60;
          nbreUnites   := ConvertirNombreEnChaine(lUniteComp, unContexte);
          result       := ComposerChainesDizainesEtUnites(nbreDizaines, nbreUnites);
        end;
      end;
    end;
     
    function ConvertirNombreEntre80et89( unEntier: TEntierBorneParle; unContexte: TContexteLangue): TNombreEnChaine;
    var
      laDizaine    : TDizainesNormales;
      lUnite       : TUnites;
      nbreDizaines : TNombreEnChaine;
      nbreUnites   : TNombreEnChaine;
    begin
      DecomposerNombre(unEntier, lUnite, laDizaine);
      case unContexte of
         clSuisseRomande : nbreDizaines := rs80SuisseRomande;
         else
           nbreDizaines := rs80ParDefault;
      end;
      nbreUnites:=ConvertirNombreEntre00et10(lUnite);
      result := ComposerChainesDizainesEtUnites(nbreDizaines, nbreUnites);
    end;
     
    function ConvertirNombreEntre90et99( unEntier: TEntierBorneParle; unContexte: TContexteLangue): TNombreEnChaine;
    var
      laDizaine    : TDizainesNormales;
      lUniteComp   : TUnitesComposees;
      lUnite       : TUnites;
      nbreDizaines : TNombreEnChaine;
      nbreUnites   : TNombreEnChaine;
    begin
      case unContexte of
        clBelgique, clSuisseRomande, clSuisseRomandeGeneve :
        begin
          lUnite       := unEntier - 90;
          nbreDizaines := rs90Belge;
          nbreUnites   := ConvertirNombreEntre00et10(lUnite);
          result := ComposerChainesDizainesEtUnites(nbreDizaines, nbreUnites);
        end
        else
        begin
          nbreDizaines := rs80ParDefault;
          lUniteComp   := unEntier - 80;
          nbreUnites   := ConvertirNombreEnChaine(lUniteComp, unContexte);
          result       := ComposerChainesDizainesEtUnites(nbreDizaines, nbreUnites);
        end;
      end;
    end;
     
    function ConvertirNombreEnChaine( unEntier: TEntierBorneParle; unContexte: TContexteLangue): TNombreEnChaine;
    begin
      case unEntier of
         00..10 : result := ConvertirNombreEntre00et10(unEntier, unContexte);
         11..19 : result := ConvertirNombreEntre11et19(unEntier, unContexte);
         20..69 : result := ConvertirNombreEntre20et69(unEntier, unContexte);
         70..79 : result := ConvertirNombreEntre70et79(unEntier, unContexte);
         80..89 : result := ConvertirNombreEntre80et89(unEntier, unContexte);
         90..99 : result := ConvertirNombreEntre90et99(unEntier, unContexte);
      end;
    end;
     
    end.

    et l'unité de test (une fenêtre):
    Code pascal : 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
    60
    61
    62
    63
    64
    unit FMainTestPyramidev;
     
    {$mode objfpc}{$H+}
     
    interface
     
    uses
      Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
      Spin, ExtCtrls, UConversionNombreLettres;
     
    type
     
      { TFormConversionNombreLettres }
     
      TFormConversionNombreLettres = class(TForm)
        ButtonConvertir: TButton;
        Label1: TLabel;
        Label2: TLabel;
        LabelResultat: TLabel;
        RadioGroupLangue: TRadioGroup;
        SpinEditNombre: TSpinEdit;
        procedure ButtonConvertirClick(Sender: TObject);
      private
        function RG2Langue( unIndex: Integer): TContexteLangue;
      public
     
      end;
     
    var
      FormConversionNombreLettres: TFormConversionNombreLettres;
     
    implementation
     
    {$R *.frm}
     
    { TFormConversionNombreLettres }
     
    procedure TFormConversionNombreLettres.ButtonConvertirClick(Sender: TObject);
    var
      laValeur   : TEntierBorneParle;
      leNombre   : TNombreEnChaine;
      leContexte : TContexteLangue;
    begin
      laValeur   := SpinEditNombre.Value;
      leContexte := RG2Langue( RadioGroupLangue.ItemIndex);
      leNombre   := ConvertirNombreEnChaine( laValeur, leContexte);
      LabelResultat.Caption := leNombre;
    end;
     
    function TFormConversionNombreLettres.RG2Langue(unIndex: Integer
      ): TContexteLangue;
    begin
      case unIndex of
        1 : result := clFrance;
        2 : result := clBelgique;
        3 : result := clQuebec;
        4 : result := clSuisseRomande;
        5 : result := clSuisseRomandeGeneve;
        else
          result := clParDefaut;
      end;
    end;
     
    end.

    avec les paramètres de configuration de la fenêtre (ce n'est pas codé en dur en Pascal moderne):
    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
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    object FormConversionNombreLettres: TFormConversionNombreLettres
      Left = 388
      Height = 240
      Top = 232
      Width = 642
      Caption = 'Conversion nombre en toutes lettres'
      ClientHeight = 240
      ClientWidth = 642
      LCLVersion = '6.3'
      object Label1: TLabel
        Left = 51
        Height = 15
        Top = 32
        Width = 47
        Caption = 'Nombre:'
        ParentColor = False
      end
      object Label2: TLabel
        Left = 51
        Height = 15
        Top = 64
        Width = 45
        Caption = 'Résultat:'
        ParentColor = False
      end
      object LabelResultat: TLabel
        Left = 104
        Height = 15
        Top = 64
        Width = 15
        Caption = 'Un'
        ParentColor = False
      end
      object ButtonConvertir: TButton
        Left = 51
        Height = 25
        Top = 104
        Width = 75
        Caption = 'Convertir'
        OnClick = ButtonConvertirClick
        TabOrder = 0
      end
      object SpinEditNombre: TSpinEdit
        Left = 104
        Height = 23
        Top = 24
        Width = 120
        MaxValue = 99
        TabOrder = 1
        Value = 1
      end
      object RadioGroupLangue: TRadioGroup
        Left = 304
        Height = 145
        Top = 24
        Width = 185
        AutoFill = True
        Caption = 'Langue:'
        ChildSizing.LeftRightSpacing = 6
        ChildSizing.EnlargeHorizontal = crsHomogenousChildResize
        ChildSizing.EnlargeVertical = crsHomogenousChildResize
        ChildSizing.ShrinkHorizontal = crsScaleChilds
        ChildSizing.ShrinkVertical = crsScaleChilds
        ChildSizing.Layout = cclLeftToRightThenTopToBottom
        ChildSizing.ControlsPerLine = 1
        ClientHeight = 125
        ClientWidth = 181
        ItemIndex = 0
        Items.Strings = (
          'Par défaut'
          'Français'
          'Belge'
          'Québecois'
          'Suisse Roman'
          'Suisse Roman (Genève)'
        )
        TabOrder = 2
      end
    end
    Cela donne à peu près ceci:
    Nom : Copie d'écran conversion nombre en lettres.png
Affichages : 302
Taille : 6,3 Ko

  13. #233
    Membre expert

    Homme Profil pro
    Consultant
    Inscrit en
    Janvier 2006
    Messages
    1 376
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Consultant

    Informations forums :
    Inscription : Janvier 2006
    Messages : 1 376
    Points : 3 583
    Points
    3 583
    Par défaut
    It existe 2 types d'humain : les développeurs et les autres.
    "La révolution informatique fait gagner un temps fou aux hommes, mais ils le passent avec leur ordinateur !"

  14. #234
    Membre régulier
    Inscrit en
    Décembre 2004
    Messages
    123
    Détails du profil
    Informations forums :
    Inscription : Décembre 2004
    Messages : 123
    Points : 97
    Points
    97
    Par défaut
    Citation Envoyé par zecreator Voir le message
    It existe 2 types d'humain : les développeurs et les autres.
    Bonjour zecreator,
    Non, c'est une profonde erreur. L'informatique est une discipline comme les autres. Ce serait comme dire: il existe 2 type d'humains, les médecins et les autres.

    Les informaticiens sont, comme tous les autres métiers, aux service de leurs clients. Le client est humain, ce n'est pas la machine, qui n'est qu'un outil. Et comme tous les autres professionnels, un informaticien doit être capable de communiquer avec son client, qui par définition, N'est PAS un informaticien.

    Cette attitude est enfantine et préjudiciable à notre discipline, à notre crédibilité, au respect qui nous est dû par nos clients, au progrès du génie logiciel qui est bien en retard par rapport au matériel.

    Bien cordialement,
    Thierryc

  15. #235
    Expert éminent sénior
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    10 720
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 10 720
    Points : 15 106
    Points
    15 106
    Par défaut Corrections ortho-typographiques
    Bonjour,
    Citation Envoyé par thierryc Voir le message
    --snip--
    dans l'unité UConversionNombreLettres :
    Code Pascal : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    // pour éviter "xxxx et un" par exemple, qui n'est plus recommandé.
    //  rsEt           = ' et ';
      rsEt           = '-et-';
     
    // le "s" n'est valide que pour 80, il est remis dans la fonction si nécessaire
    //  rs80ParDefault    = 'quatre-vingts';
      rs80ParDefault    = 'quatre-vingt';
     
    // ajout au début de la fonction ComposerChainesDizainesEtUnites :
      if (lUnite = rsZero) and (laDizaine = rs80ParDefault) then
        laDizaine := laDizaine + 's';

    Testé sous Lazarus, ça devrait être OK dans tout ce qui compile du Pascal.

    PS : non testé avec les langues autres que le français de France, car je ne connais pas leurs règles.
    Il a à vivre sa vie comme ça et il est mûr sur ce mur se creusant la tête : peutêtre qu'il peut être sûr, etc.
    Oui, je milite pour l'orthographe et le respect du trait d'union à l'impératif.
    Après avoir posté, relisez-vous ! Et en cas d'erreur ou d'oubli, il existe un bouton « Modifier », à utiliser sans modération
    On a des lois pour protéger les remboursements aux faiseurs d’argent. On n’en a pas pour empêcher un être humain de mourir de misère.
    Mes 2 cts,
    --
    jp

  16. #236
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 469
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 469
    Points : 6 102
    Points
    6 102
    Par défaut
    Thierryc,

    Merci pour le code en Pascal mais, en le lisant, de ce que je comprends, les bornes n'y sont pas vérifiées à la compilation.
    Prenons l'exemple suivant :
    Code pascal : 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
     
    type TEntierBorneParle = 0..99;
     
    [...]
     
    function ConvertirNombreEntre11et19( unEntier: TEntierBorneParle; unContexte: TContexteLangue): TNombreEnChaine;
    begin
      case unEntier of
         11 : result := rsOnze;
         12 : result := rsDouze;
         13 : result := rsTreize;
         14 : result := rsQuatorze;
         15 : result := rsQuinze;
         16 : result := rsSeize;
         17..19 : result := rsDix + rsTiretNombre + ConvertirNombreEntre00et10( unEntier mod 10);
         else
           raise EConversioNombre.Create(eNombrePasCompris);
      end;
    end;
    ConvertirNombreEntre11et19 prend en paramètre un entier qui peut être entre 0 et 99 et vérifie à l'exécution que l'entier est entre 11 et 19.

    Par contre, dans la version de mon code avec les templates, la fonction chaineNombreEntre1Et19 prend en paramètre un entier de type EntierBorne<1, 19>. Si on lui passe directement en paramètre un entier de type EntierBorne<1, 99>, alors on a une erreur de compilation, car EntierBorne<1, 99> n'est pas directement convertible en EntierBorne<1, 19>.

    Ce qu'il faut, c'est que le compilateur n'accepte la conversion de EntierBorne<1, 99> vers EntierBorne<1, 19> que s'il est sûr que nombre <= 19.

    Dans la version de mon code avec les templates, la fonction chaineNombreEntre1Et99 est de la forme :
    Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    std::string chaineNombreEntre1Et99(EntierBorne<1, 99> nombre, bool adjNumOrdinal, bool septanteHuitanteNonante)
    {
        return ifLessThanThenElse<20>(
            nombre,
            routine capable de prendre en paramètre un EntierBorne<1, 19>,
            routine capable de prendre en paramètre un EntierBorne<20, 99>
        );
    }
    Si nombre < 20, alors ifLessThanThenElse<20> force la conversion de nombre en EntierBorne<1, 19> puis le passe à la routine en 2e paramètre.
    Sinon, ifLessThanThenElse<20> force la conversion de nombre en EntierBorne<20, 99> puis le passe à la routine en 3e paramètre.

    Prenons un autre passage de mon code :
    Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    [&](EntierBorne<81, 99> nombre) {
        resultat += u8'-';
        resultat += chaineNombreEntre1Et19(moins<80>(nombre));
    }
    Dans cette portion de code, nombre est de type EntierBorne<81, 99>. moins<80> le prend en paramètre et retourne un entier de type EntierBorne<1, 19>.

    Au final, dans le code de chaineNombreEntre1Et19, il n'y a pas besoin de prévoir le cas où le nombre en paramètre serait hors des bornes, car le compilateur empêche que cela se produise.

    Citation Envoyé par Jipété Voir le message
    Code Pascal : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    // pour éviter "xxxx et un" par exemple, qui n'est plus recommandé.
    //  rsEt           = ' et ';
      rsEt           = '-et-';
    Personnellement, je gère ça dans une fonction de plus haut niveau.
    Mes fonctions de bas niveau comme chaineNombreEntre1Et99 ne tiennent pas compte des rectifications orthographique de 1990, qui demandent d'écrire des tirets partout.
    Par contre, dans le code de plus haut niveau qui convertit un nombre en lettres, j'ai une option qui permet de remplacer les espaces par des tirets (si l'utilisateur le veut).

    D'ailleurs, pour ceux qui veulent approfondir :


    Citation Envoyé par Jipété Voir le message
    Code Pascal : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    // le "s" n'est valide que pour 80, il est remis dans la fonction si nécessaire
    //  rs80ParDefault    = 'quatre-vingts';
      rs80ParDefault    = 'quatre-vingt';
    D'ailleurs, on écrit "quatre-vingts pages", mais "la page quatre-vingt" (sans "s"). Dans le deuxième cas, "quatre-vingt" est un adjectif numéral ordinal. C'est pour ça que ma fonction chaineNombreEntre1Et99 a un paramètre adjNumOrdinal.

  17. #237
    Membre régulier
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2017
    Messages
    36
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2017
    Messages : 36
    Points : 95
    Points
    95
    Par défaut
    Citation Envoyé par thierryc Voir le message
    ci-dessous le source de l'unité de conversion en Pascal.
    Merci de la traduction. Quelques questions et remarques (je ne connais pas Pascal, donc soyez indulgents) :

    1) Il y a une incohérence : TUnites va de 0 à 9 mais dans ConvertirNombreEntre00et10 on teste le nombre entre 0 et 10 inclus. Du coup comment se fait-il que le compilateur ne donne pas d'erreur ou de warning (je suppose car je n'ai pas compilé) ?

    2) Si on va vraiment de 0 à 9 pour les valeurs possible de TUnites, cela veut dire qu'un objet de ce type ne peut rien contenir d'autre, par construction non ? Alors pourquoi tester et lancer une exception si on est supérieur (toujours dans ConvertirNombreEntre00et10) ?

    3) Il y a une dépendance cyclique entre ConvertirNombreEntre90et99 et ConvertirNombreEnChaine : elles s'appellent mutuellement.

    4) Que se passe-t-il si l'utilisateur rentre 101 dans l'interface - s'il le peut. Et dans le deux cas, où cette gestion est-elle codé ?

  18. #238
    Membre régulier
    Inscrit en
    Décembre 2004
    Messages
    123
    Détails du profil
    Informations forums :
    Inscription : Décembre 2004
    Messages : 123
    Points : 97
    Points
    97
    Par défaut Correction du Pascal et ajout de l'Ada.
    Citation Envoyé par _Bérenger_ Voir le message
    Merci de la traduction. Quelques questions et remarques
    Bravo à tous ceux qui ont lu le code et qui ont vu une des deux erreurs que j'y avais glissée (malheureusement non intentionnellement ...). Cela est possible car je n'active pas en production le "range checking". C'est une option que l'on peut activer ou non. C'est bien entendu une erreur de ma part sur un développement non testé que de ne pas l'avoir activé.
    Quand l'option est activée, une exception est levée lorsque le nombre sort de l'intervalle. Cette exception est d'ailleurs automatiquement attrapée par l'IHM pour informer l'utilisateur, et lui permettre de poursuivre son travail (et d'informer le support IT ... moi :-().

    La première erreur est de limiter à 0..9 les unités, alors que la fonction qui traduit les unités se charge aussi de 10.
    La deuxième erreur est de limiter les dizaines normales à 2..6, et de les utiliser quand même dans les procédures qui traduisent 70 et au-delà.

    C'est une règle de bonne programmation que de mettre systématiquement la clause "else" dans un "case of". Notamment en cas d'évolution du code. Dans ce cas-ci, elle n'est pas utile immédiatement, mais elle peut le devenir par la suite en maintenance. Ou plutôt son absence peut générer des défauts en maintenance.

    L'IHM limite les nombres de 0 à 99. Il n'est pas possible à l'utilisateur de rentrer un autre chiffre, ni des lettres, ni des flottants, etc. Ceinture et bretelles ...

    Ce n'est pas une dépendance cyclique, c'est une récursion entre deux procédures. Aucun problème particulier à l'intérieur d'une unité, pourvu que la récursion s'arrête. La dépendance cyclique est une notion qui s'applique aux modules.

    Je n'ai pas pris en compte la nature ordinale ou cardinale du nombre. Je laisse le soin au lecteur de le faire, à titre d'exercice et pour voir si c'est difficile de faire évoluer le code.

    Les tirets et les espaces ne sont pas optionnels dans la traduction d'un nombre vers des lettres pour les cardinaux dans ce source. C'est également très simple à modifier si nécessaire.

    Ci dessous la même traduction en Ada (uniquement le corps du paquetage). C'est d'ailleurs à la traduction en Ada que j'ai trouvé mes 2 erreurs... Le fichier attaché comporte tout le projet Ada et le fichier des résultats de tests.

    Je vais regarder maintenant la version orientée-objet (et ajouter la variabilité des tirets et des espaces ainsi que le choix ordinal ou cardinal) avant de m'attaquer à la version orientée-composant.

    Bonne lecture...

    Code ada : 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
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    with ada.strings.unbounded;
    use ada.strings.unbounded;
    package body UConversionNombreLettres is
     
    subtype TUnites is integer range 0..10;
    subtype TUnitesComposees is integer range 0..19;
    subtype TDizaines is integer range 0..9;
     
    --toutes les chaines sont déclarées en constantes
      --séparateurs de nombres
      rsEspaceNombre : constant TNombreEnChaine := To_Unbounded_String(" ");
      rsTiretNombre  : constant TNombreEnChaine := To_Unbounded_String("-");
      rsEt           : constant TNombreEnChaine := To_Unbounded_String(" et ");
     
      --Unités:
      rsZero            : constant TNombreEnChaine :=  To_Unbounded_String("zero");
      rsUn              : constant TNombreEnChaine :=  To_Unbounded_String("un");
      rsDeux            : constant TNombreEnChaine :=  To_Unbounded_String("deux");
      rsTrois           : constant TNombreEnChaine :=  To_Unbounded_String("trois");
      rsQuatre          : constant TNombreEnChaine :=  To_Unbounded_String("quatre");
      rsCinq            : constant TNombreEnChaine :=  To_Unbounded_String("cinq");
      rsSix             : constant TNombreEnChaine :=  To_Unbounded_String("six");
      rsSept            : constant TNombreEnChaine :=  To_Unbounded_String("sept");
      rsHuit            : constant TNombreEnChaine :=  To_Unbounded_String("huit");
      rsNeuf            : constant TNombreEnChaine :=  To_Unbounded_String("neuf");
      --Entre dix et dix-neuf
      rsDix             : constant TNombreEnChaine :=  To_Unbounded_String("dix");
      rsOnze            : constant TNombreEnChaine :=  To_Unbounded_String("onze");
      rsDouze           : constant TNombreEnChaine :=  To_Unbounded_String("douze");
      rsTreize          : constant TNombreEnChaine :=  To_Unbounded_String("treize");
      rsQuatorze        : constant TNombreEnChaine :=  To_Unbounded_String("quatorze");
      rsQuinze          : constant TNombreEnChaine :=  To_Unbounded_String("quinze");
      rsSeize           : constant TNombreEnChaine :=  To_Unbounded_String("seize");
      --dizaines
      rsVingt           : constant TNombreEnChaine :=  To_Unbounded_String("vingt");
      rsTrente          : constant TNombreEnChaine :=  To_Unbounded_String("trente");
      rsQuarante        : constant TNombreEnChaine :=  To_Unbounded_String("quarante");
      rsCinquante       : constant TNombreEnChaine :=  To_Unbounded_String("cinquante");
      rsSoixante        : constant TNombreEnChaine :=  To_Unbounded_String("soixante");
     
      --entiers par contexte..
      rs70ParDefault    : constant TNombreEnChaine :=  To_Unbounded_String("soixante-dix");
      rs70Belge         : constant TNombreEnChaine :=  To_Unbounded_String("septante");
     
      rs80ParDefault    : constant TNombreEnChaine :=  To_Unbounded_String("quatre-vingts");
      rs80SuisseRomande : constant TNombreEnChaine :=  To_Unbounded_String("huitante");
     
      rs90ParDefault    : constant TNombreEnChaine :=  To_Unbounded_String("quatre-vingt-dix");
      rs90Belge         : constant TNombreEnChaine :=  To_Unbounded_String("nonante");
     
      --messaqes d'erreur:
      eUniteNonComprise : constant Unbounded_String :=  To_Unbounded_String("unité non comprise");
      eDizaineNonComprise : constant Unbounded_String :=  To_Unbounded_String("dizaine non comprise");
      eNombrePasCompris : constant Unbounded_String :=  To_Unbounded_String("Nombre pas compris");
     
       -----------------------------------------------------------------------------
       procedure DecomposerNombre( unEntier : in TEntierBorneParle;
                                   lUnite: out TUnites; laDizaine: out TDizaines) is
       begin
          lUnite    := unEntier mod 10;
          laDizaine := unEntier / 10;
       end;
     
       -----------------------------------------------------------------------------
       function ConvertirNombreEntre00et10( unEntier: in TUnites;
                                            unContexte: in TContexteLangue := clParDefaut)
                                           return TNombreEnChaine
       is
          result: TNombreEnChaine := rsUn;
       begin
          case unEntier is
          when 0  => result := rsZero;
          when 1  => result := rsUn;
          when 2  => result := rsDeux;
          when 3  => result := rsTrois;
          when 4  => result := rsQuatre;
          when 5  => result := rsCinq;
          when 6  => result := rsSix;
          when 7  => result := rsSept;
          when 8  => result := rsHuit;
          when 9  => result := rsNeuf;
          when 10 => result := rsDix;
          when others => raise EConversioNombre;
          end case;
          return result;
       end;
     
       -----------------------------------------------------------------------------
       function ConvertirDizaines( uneDizaine: TDizaines;
                                   unContexte: TContexteLangue := clParDefaut)
                                  return TNombreEnChaine
       is
          result: TNombreEnChaine;
       begin
          case uneDizaine is
             when 2 => result := rsVingt;
             when 3 => result := rsTrente;
             when 4 => result := rsQuarante;
             when 5 => result := rsCinquante;
             when 6 => result := rsSoixante;
             when others => raise EConversioNombre;
          end case;
          return result;
       end ConvertirDizaines;
     
       -----------------------------------------------------------------------------
       function ComposerChainesDizainesEtUnites( laDizaine: TNombreEnChaine;
                                                 lUnite: TNombreEnChaine)
                                                return TNombreEnChaine
       is
          result: TNombreEnChaine;
       begin
          if lUnite = rsZero then
             result := laDizaine;
          else if ((lUnite=rsUn) or (lUnite=rsOnze)) and (laDizaine/=rs80ParDefault) then
                result := laDizaine & rsEt & lUnite;
             else
                result := laDizaine & rsTiretNombre & lUnite;
             end if;
          end if;
          return result;
       end ComposerChainesDizainesEtUnites;
     
       -----------------------------------------------------------------------------
       function ConvertirNombreEntre11et19( unEntier: TEntierBorneParle;
                                            unContexte: TContexteLangue)
                                           return TNombreEnChaine
       is
          result : TNombreEnChaine;
       begin
          case unEntier is
             when 11 => result := rsOnze;
             when 12 => result := rsDouze;
             when 13 => result := rsTreize;
             when 14 => result := rsQuatorze;
             when 15 => result := rsQuinze;
             when 16 => result := rsSeize;
             when 17..19 => result := rsDix & rsTiretNombre & ConvertirNombreEntre00et10( unEntier mod 10);
             when others =>
                raise EConversioNombre;
          end case;
          return result;
       end ConvertirNombreEntre11et19;
     
       -----------------------------------------------------------------------------
       function ConvertirNombreEntre20et69( unEntier: TEntierBorneParle;
                                            unContexte: TContexteLangue)
                                           return TNombreEnChaine
       is
          laDizaine    : TDizaines;
          lUnite       : TUnites;
          nbreDizaines : TNombreEnChaine;
          nbreUnites   : TNombreEnChaine;
          result       : TNombreEnChaine;
       begin
          DecomposerNombre(unEntier, lUnite, laDizaine);
          nbreDizaines := ConvertirDizaines(laDizaine);
          nbreUnites   := ConvertirNombreEntre00et10(lUnite);
          result       := ComposerChainesDizainesEtUnites(nbreDizaines, nbreUnites);
          return result;
       end ConvertirNombreEntre20et69;
     
       -----------------------------------------------------------------------------
       function ConvertirNombreEntre70et79( unEntier: TEntierBorneParle;
                                            unContexte: TContexteLangue)
                                           return TNombreEnChaine
       is
          lUnite       : TUnites;
          lUniteComp   : TUnitesComposees;
          nbreDizaines : TNombreEnChaine;
          nbreUnites   : TNombreEnChaine;
          result       : TNombreEnChaine;
       begin
          case unContexte is
             when clBelgique | clSuisseRomande | clSuisseRomandeGeneve =>
                begin
                   lUnite:= unEntier - 70;
                   nbreDizaines  := rs70Belge;
                   nbreUnites    := ConvertirNombreEntre00et10(lUnite);
                   result := ComposerChainesDizainesEtUnites(nbreDizaines, nbreUnites);
                end;
             when others =>
                begin
                   nbreDizaines := rsSoixante;
                   lUniteComp   := unEntier - 60;
                   nbreUnites   := ConvertirNombreEnChaine(lUniteComp, unContexte);
                   result       := ComposerChainesDizainesEtUnites(nbreDizaines, nbreUnites);
                end;
          end case;
          return result;
       end ConvertirNombreEntre70et79;
     
       function ConvertirNombreEntre80et89( unEntier: TEntierBorneParle;
                                            unContexte: TContexteLangue)
                                           return TNombreEnChaine
       is
          laDizaine    : TDizaines;
          lUnite       : TUnites;
          nbreDizaines : TNombreEnChaine;
          nbreUnites   : TNombreEnChaine;
          result       : TNombreEnChaine;
       begin
          DecomposerNombre(unEntier, lUnite, laDizaine);
          case unContexte is
             when clSuisseRomande => nbreDizaines := rs80SuisseRomande;
             when others => nbreDizaines := rs80ParDefault;
          end case;
          nbreUnites:=ConvertirNombreEntre00et10(lUnite);
          result := ComposerChainesDizainesEtUnites(nbreDizaines, nbreUnites);
          return result;
       end ConvertirNombreEntre80et89;
     
       function ConvertirNombreEntre90et99( unEntier: TEntierBorneParle;
                                            unContexte: TContexteLangue)
                                           return TNombreEnChaine
       is
          laDizaine    : TDizaines;
          lUniteComp   : TUnitesComposees;
          lUnite       : TUnites;
          nbreDizaines : TNombreEnChaine;
          nbreUnites   : TNombreEnChaine;
          result       : TNombreEnChaine;
       begin
          case unContexte is
          when clBelgique | clSuisseRomande| clSuisseRomandeGeneve =>
             begin
                lUnite       := unEntier - 90;
                nbreDizaines := rs90Belge;
                nbreUnites   := ConvertirNombreEntre00et10(lUnite);
                result := ComposerChainesDizainesEtUnites(nbreDizaines, nbreUnites);
             end;
          when others =>
             begin
                nbreDizaines := rs80ParDefault;
                lUniteComp   := unEntier - 80;
                nbreUnites   := ConvertirNombreEnChaine(lUniteComp, unContexte);
                result       := ComposerChainesDizainesEtUnites(nbreDizaines, nbreUnites);
             end;
          end case;
          return result;
       end ConvertirNombreEntre90et99;
       --TO BE CONTINUED
     
       -----------------------------
       -- ConvertirNombreEnChaine --
       -----------------------------
       function ConvertirNombreEnChaine
         (unEntier: in TEntierBorneParle;
          unContexte: in TContexteLangue)
          return TNombreEnChaine
       is
          result : TNombreEnChaine := To_Unbounded_String("Unknown value!");
       begin
          case unEntier is
          when 00..10 => result := ConvertirNombreEntre00et10(unEntier, unContexte);
          when 11..19 => result := ConvertirNombreEntre11et19(unEntier, unContexte);
          when 20..69 => result := ConvertirNombreEntre20et69(unEntier, unContexte);
          when 70..79 => result := ConvertirNombreEntre70et79(unEntier, unContexte);
          when 80..89 => result := ConvertirNombreEntre80et89(unEntier, unContexte);
          when 90..99 => result := ConvertirNombreEntre90et99(unEntier, unContexte);
          end case;
          return result;
       end ConvertirNombreEnChaine;
    end UConversionNombreLettres;
    Fichiers attachés Fichiers attachés

  19. #239
    Expert éminent sénior

    Homme Profil pro
    pdg
    Inscrit en
    Juin 2003
    Messages
    5 749
    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 749
    Points : 10 666
    Points
    10 666
    Billets dans le blog
    3
    Par défaut
    Citation Envoyé par thierryc Voir le message
    Ada a été développé à la demande du DoD comme langage généraliste et il était destiné à remplacer tous les langages
    Et ça ne s'est pas produit. La question est : pourquoi ? Quel est votre point de vue là dessus ? Je veux dire, point de vue autre qu'une variante de "les gens sont stupides". Il y forcément des lacunes importantes pour que le langage ait échoué de cette façon, et nier ces lacunes c'est condamner le langage à les conserver. Les nommer, c'est commencer à les résoudre.

    Citation Envoyé par thierryc Voir le message
    Les qualités même qui font que Ada a été retenu pour Rosetta et Ariane 6 en font un outil parfaitement adapté pour aller vite et durer longtemps.
    Le bug en ADA sur Ariane 5 est un des plus coûteux de tous les temps. A l'inverse les fusées de SpaceX qui donnent en ce moment même des sueurs froides aux responsables de l'ESA ont été développées en quelques années... en C++. La NASA a basculé avec succès vers C++ pour sa mission martienne Curiosity qui nous envoie encore aujourd'hui des vidéos. Nombre d'appareils médicaux, depuis le pousse seringues jusqu'au scanner IRM tournent avec C++. Mêmes les auteurs de GCC, grands aficionados du langage C, ont basculé vers C++ il y a quelques années déjà, ce qui implique que c'est grâce à C++ qu'on a des compilos C et ADA :p

    Tout cela ne prouve pas que C++ est meilleure qu'ADA ou qu'ADA n'est pas un bon langage. Mais cela prouve qu'on peut faire avec succès des choses colossales et très critiques en C++. Et je dirais même, étant donné que C++ est utilisé pour le pilotage autonome des voitures, cela prouve que C++ est un langage qui tient la route^^

    Citation Envoyé par thierryc Voir le message
    Moi, j'aime bien quand les logiciels que j'ai fait développer sont utilisés par le client pendant 10, 15 ou 20 ans ou plus, et qu'ils restent maintenables et portables après tout ce temps.
    Mais s'il y a bien un langage qui tient cette promesse c'est C++ ! Qt par exemple a plus de 20 ans et une une lib de plusieurs millions de LOC de grande qualité. De gros soft C++ anciens et de très bonne facture, il y en a énormément, à commencer par le navigateur web que vous avez devant vous en ce moment même, ou la suite office de Microsoft. Google, c'est 250 millions de LOC en C++! Microsoft ne doit pas en être loin. Facebook est aussi un grand utilisateur de C++. Résultat : ces sociétés génèrent des milliards de revenus et sont des acteurs majeurs de la planète logicielle. Allez donc les voir en leur expliquant que leur choix relève de la faute lourde et qu'ils doivent tout réécrire en ADA! C'est ri-di-cu-le.

    L'outillage en C++ est considérable de nos jours. On peut par exemple automatiser la réécriture d'une base de code ancienne vers un nouveau standard. Et cela gratuitement. Si ce dernier point suscite le scepticisme chez vous, cela produit exactement l'effet inverse au sein d'une communauté. Les outils open source sont la garantie de pouvoir continuer à compiler et maintenir notre code dans 20 ans. La communauté est assez mature et responsable pour avoir compris qu'ils ont inétrêt à unir leurs forces sur des outils communs qui sont partagés. Alors qu'une boite privée peur couler et disparaître avec son outil commercial. Il n'y a qu'à regarder les déboire de Borland. A mon niveau, j'ai appris à programmer en première année de mes études avec le langage ADA au moyen d'un compilateur/IDE qui n'existe plus aujourd'hui !! Où est donc la pérennité promise pour mon code ? A l'inverse, mes premiers programmes en C++/Qt sont recompilables aujourd'hui.

    Citation Envoyé par thierryc Voir le message
    Il y a un réel manque de rationalité dans notre métier.
    Dans votre métier. Car pour moi, vous en êtes l'exemple même de ce que vous critiquez. C'est vieux comme le monde : l'histoire de l'oeil et de la poutre. A vous lire c'est limpide que le comportement que vous critiquez chez les autres n'est que le reflet de votre propre attitude, selon le bon vieux principe du "qui se ressemble s'assemble". Vous êtes très fort pour troller sur la base d'anecdotes invérifiables sur vos exploits d’antan et l'incompétence des développeurs C++ qui vous entourent. Mais pour donner des éléments factuels vérifiables, là c'est autre chose. Ou est la rationalité là dedans ?

    Et puis nous n'avons pas parlé de la transformation importante du monde de la programmation qui est à l'oeuvre depuis 10 ans et qui semble vous échapper : déclin de la POO, retour du fonctionnel, abandon des exceptions, etc... C++ a su se moderniser et se simplifier dans ce sens. Il n'y a qu'à comparer la métaprogrammation en C++98 avec ce que l'on fait en C++14.

    Par exemple, avec la fmt lib, le code suivant échoue à compiler :

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    fmt::prinf("Bonjour {}, bienvenue sur {}", userName); // 2eme paramètre manquant

    Le code qui (in)valide le nombre d'arguments et qui traditionnellement est exécuté au runtime est ici aussi exécuté au moment de la compilation.

  20. #240
    Membre régulier
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2017
    Messages
    36
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2017
    Messages : 36
    Points : 95
    Points
    95
    Par défaut
    Citation Envoyé par Aurelien.Regat-Barrel Voir le message
    C++ est utilisé pour le pilotage autonome des voitures, cela prouve que C++ est un langage qui tient la route^^

Discussions similaires

  1. Pourquoi les langages interprétés sont-ils préférés pour l'analyse de données ?
    Par User23 dans le forum Statistiques, Data Mining et Data Science
    Réponses: 1
    Dernier message: 12/05/2016, 21h18
  2. Les langages statiques sont-ils trop sophistiqués et complexes ?
    Par Katleen Erna dans le forum Actualités
    Réponses: 53
    Dernier message: 20/01/2013, 10h06
  3. Réponses: 2
    Dernier message: 11/05/2010, 19h36
  4. Réponses: 2
    Dernier message: 06/05/2007, 22h37
  5. Pourquoi les mails ne sont ils pas envoyés?
    Par Sunsawe dans le forum Développement
    Réponses: 3
    Dernier message: 12/04/2007, 23h49

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