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

Caml Discussion :

[Débutant] Division euclidienne.


Sujet :

Caml

  1. #1
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2015
    Messages
    43
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Enseignement

    Informations forums :
    Inscription : Octobre 2015
    Messages : 43
    Points : 17
    Points
    17
    Par défaut [Débutant] Division euclidienne.
    Salut à tous !

    J'essaye d'apprendre le langage Caml est j'ai écrit un programme pour calculer le quotient et le reste d'une division euclidienne dans Z sans utiliser les opérateurs que Caml possède déjà.

    Idée :
    Déjà je ne prend pas en compte les diviseurs négatifs parce que je souhaite que le reste soit toujours plus petit que le diviseur.
    Mais si le divisé est négatif alors je le rend positif puis je multiplierait le quotient par -1.
    Pour calculer ce quotient je vais compter combien de fois il y a le diviseur dans le divisé.
    Pour calculer le reste j'utilise la formule de la division euclidien de a par b qui est r = a-qb avec q le quotient.
    Puis j'affiche.

    Au passage j'ai tout référencé au moins je peux modifier toutes mes valeurs quand j'ai envie.

    Voici mon programme :
    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
    let division_euclidien_Z a b = 
    	let signe = ref 1 and nb_de_fois = ref 1 and A = ref a and B = ref b and R = ref 1 in
    		if(!B<0) then failwith "Diviseur négatif"
    		if(!A<0) then 
    			begin 
    				!A := (-1)*!A;
    				signe := ref -1;
    			end;
    	while (!nb_de_fois * !b < !A || !nb_de_fois * !b = !A) do incr nb_de_fois; done;
    	!nb_de_fois := (nb_de_fois -1)*!signe;
    	!R := !A - (!nb_de_fois * !b);
    	print_string "La division euclidien de ";
    	printf_int !A;
    	print_string "par ";
    	print_int !B*!signe;
    	print_string " :";
    	print_newline;
    	print_string "Quotient : " print_int !nb_de_fois;
    	print_newline;
    	print_string "Reste : " print_int !R;
    	;;
    Je n'arrive pas à déterminer les erreurs de synthaxe pouvez vous me donner un coup de main s'il vous plait.

  2. #2
    Membre habitué
    Profil pro
    Inscrit en
    Octobre 2010
    Messages
    87
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2010
    Messages : 87
    Points : 172
    Points
    172
    Par défaut Programmation fonctionnelle, mon amour
    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
    let division_euclidien_Z a b = 
    	let signe = ref 1 and nb_de_fois = ref 1 and A = ref a and B = ref b and R = ref 1 in
    		if(!B<0) then failwith "Diviseur négatif"
    		if(!A<0) then 
    			begin 
    				!A := (-1)*!A;
    				signe := ref -1;
    			end;
    	while (!nb_de_fois * !b < !A || !nb_de_fois * !b = !A) do incr nb_de_fois; done;
    	!nb_de_fois := (nb_de_fois -1)*!signe;
    	!R := !A - (!nb_de_fois * !b);
    	print_string "La division euclidien de ";
    	printf_int !A;
    	print_string "par ";
    	print_int !B*!signe;
    	print_string " :";
    	print_newline;
    	print_string "Quotient : " print_int !nb_de_fois;
    	print_newline;
    	print_string "Reste : " print_int !R;
    	;;
    Plusieurs choses (syntaxiquement parlant). Les majuscules en début de nom de variable sont réservées :
    • Aux modules
    • Aux constructeurs de types algébriques (type t = A | B)


    Vous ne pouvez donc pas écrire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    let A = ref a and B = ref b and R = ref 1
    mais
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    let a = ref a and b = ref b and r = ref 1
    De plus, si vous écrivez
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if e1 then i1 if e2 then i2;
    (i1 et i2 étant des instructions (type de retour, unit)) alors il ne faut surtout pas oublier d'ajouter un point-virgule après i1 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if e1 then i1; if e2 then i2;
    Ici, je pense que vous vous êtes un peu emmêlé les pinceaux, il faut écrire idem pour
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    !nb_de_fois := (nb_de_fois * !b)
    qui devient
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    nb_de_fois := !nb_de_fois * !b
    et pour
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    !r := !a - (!nb_de_fois * !b);
    qui devient
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    r := !a - (!nb_de_fois * !b);
    (l'opération r := e signifie que la référence r pointe dorénavant sur le contenu e, c'est donc la référence qu'il faut mettre à gauche de l'opérateur et non pas son contenu, donc r et pas !r)
    Dans le même ordre d'idée, devient car à droite de l'opérateur on doit retrouver le contenu (ici, -1) et non pas la référence (ici ref -1).

    Ici, plusieurs erreurs. A l'analyse lexicale, *! pourrait être un opérateur donc OCaml va le lire comme tel et vous dire qu'il ne connaît pas l'opérateur *!. Il faut donc mettre une séparation entre les deux. De plus, les opérateurs écrits de la sorte sont infixes, c'est à dire qu'on évalue ce qu'il y a à gauche de l'opérateur puis ce qu'il y a à droite puis on applique l'opérateur entre les deux évaluations, donc votre code va être interprété comme ceci
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    (print_int !b) *! (!signe)
    et ça va râler. Il faut donc écrire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    print_int (!b * !signe)
    est une fonction, si vous ne lui donnez pas d'argument elle ne va rien faire (c'est ce qu'on appelle une application partielle). Il faut donc écrire Enfin,
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    print_string "Quotient : " print_int !nb_de_fois;
    Vous avez oublié le point-virgule entre les deux applications. Il faut donc écrire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    print_string "Quotient : "; print_int !nb_de_fois;
    Idem pour
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    print_string "Reste : " print_int !r;
    qui devient
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    print_string "Reste : "; print_int !r;
    Et votre code devient donc (même si je ne comprends pas pourquoi vous multipliez !b par !signe et pas !a vu que !signe vient de !a) :

    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
    let division_euclidien_Z a b = 
    	let signe = ref 1 and nb_de_fois = ref 1 and a = ref a and b = ref b and r = ref 1 in
    		if(!b<0) then failwith "Diviseur négatif";
    		if(!a<0) then 
    			begin 
    				a := (-1) * !a;
    				signe := -1;
    			end;
    	while (!nb_de_fois * !b < !a || !nb_de_fois * !b = !a) do incr nb_de_fois; done;
    	nb_de_fois := (!nb_de_fois -1) * !signe;
    	r := !a - (!nb_de_fois * !b);
    	print_string "La division euclidien de ";
    	print_int !a;
    	print_string "par ";
    	print_int (!b * !signe);
    	print_string " :";
    	print_newline;
    	print_string "Quotient : "; print_int !nb_de_fois;
    	print_newline ();
    	print_string "Reste : "; print_int !r;
    	;;
    MAIS... C'EST DEGUEU !

    Vous êtes en train d'apprendre OCaml et j'en suis très heureux. Je ne fais pas partie des personnes qui crachent sur les aspects impératifs d'OCaml mais vous avez dû vous en rendre compte, c'est un langage fonctionnel a priori. Votre code ne prend en compte rien de tout ça. Je me suis donc permis d'écrire votre code d'une facon bien plus concise et fonctionnelle :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    let div_eucl a b =
      let signe, ua = if a < 0 then -1, -a else 1, a in
      if b < 0 then failwith "Diviseur négatif";
      let rec dr ai q =
        let ai' = ai + b in
        if ai' > ua then (q, ua - ai)
        else dr ai' (q+1)
      in 
      let (q, r) = dr 0 0 in
      let q = signe * q in
      Printf.printf "%d = %d * %d + %d\n" a b q r

  3. #3
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2015
    Messages
    43
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Enseignement

    Informations forums :
    Inscription : Octobre 2015
    Messages : 43
    Points : 17
    Points
    17
    Par défaut
    Salut TchoubiTchoub.

    Un gros j'aime un votre réponse ! J'apprends en autodidacte et ce genre d'intervention m'aide énormément.
    Je suis en train de réécrire un code en essayant d'utiliser des fonctions auxiliaires et en me rapportant à vos remarques, j'espère arriver à un code qui ressemble au votre, je vous tiens au courant.

  4. #4
    Membre habitué
    Profil pro
    Inscrit en
    Octobre 2010
    Messages
    87
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2010
    Messages : 87
    Points : 172
    Points
    172
    Par défaut
    N'hésitez pas à me poser des questions sur le code que j'ai écrit. Tout n'est pas forcément évident dès le départ.

  5. #5
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2015
    Messages
    43
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Enseignement

    Informations forums :
    Inscription : Octobre 2015
    Messages : 43
    Points : 17
    Points
    17
    Par défaut
    Je vais devoir arrêter de coder pour ce soir, demain je dois être en forme pour réviser les maths lol.

    Voici un peu mon avancement :

    Idée : J'ai séparé le problème en plusieurs fonctions.
    Je viens de finir la fonction quotient qui est centrale car par la formule de la division euclidienne je vais pouvoir conclure. Le quotient sera :
    - Exactement le résultat de la fonction quotient si le divisé est positif.
    - L'opposé moins un du résultat de la fonction quotient si le divisé est négatif.

    Et pour le reste ce sera une simple addition ou soustraction.

    Voici déjà la fonction quotient :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    let valeur_absolue_int a = if a > 0 then a else -a;;
     
    let quotient a b = 
    	let rec aux a b acc = match b*acc with
    		| 0 -> failwith("bug");
    		| e when e <= valeur_absolue_int(a) -> aux a b (acc + 1)
    		| e when e > valeur_absolue_int(a) -> acc-1;
    	in aux a b 1;;

  6. #6
    Membre habitué
    Profil pro
    Inscrit en
    Octobre 2010
    Messages
    87
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2010
    Messages : 87
    Points : 172
    Points
    172
    Par défaut
    Citation Envoyé par CechD Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    let quotient a b = 
    	let rec aux a b acc = match b*acc with
    		| 0 -> failwith("bug");
    		| e when e <= valeur_absolue_int(a) -> aux a b (acc + 1)
    		| e when e > valeur_absolue_int(a) -> acc-1;
    	in aux a b 1;;
    C'est correct mais je vais chipoter. Dans la déclaration de la fonction récursive interne vous lui donnez trois paramètres, a, b et acc. Ce n'est pas faux mais dans l'appel récursif, a et b ne changent pas. Donc vous n'avez pas besoin de les mettre en paramètre puisqu'ils existent déjà au niveau au-dessus. De plus, (mais je sais que certains ne s'accordent pas avec moi sur ce point), je trouve illisible le pattern matching sur des valeurs entières (et d'un point de vue algorithmique l'avoir écrit comme ça sera compilé en if then else) (le pattern matching consiste essentiellement à reconnaître des motifs. )

    J'aurais donc écrit votre code de la façon suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    let quotient a b =
      let va = valeur_absolue_int a in 
      let rec aux acc = 
         let e = b*acc in
         if e = 0 then failwith("bug");
         else if  e <= va then aux (acc + 1)
         else if e > va -> acc-1;
      in aux 1;;
    Par contre, un grand bravo, vous avez fait de la récursion terminale !

Discussions similaires

  1. Algorithme d'euclide étendu
    Par eldiablo7 dans le forum Scheme
    Réponses: 1
    Dernier message: 25/10/2009, 10h11
  2. Algorithme d'Euclide et "plus grand commun diviseur"
    Par Jerome Briot dans le forum Téléchargez
    Réponses: 0
    Dernier message: 04/09/2009, 18h56
  3. [débutant] algorithme de calcul de prix HT/TTC
    Par keren dans le forum Algorithmes et structures de données
    Réponses: 2
    Dernier message: 06/06/2009, 12h14
  4. Algorithme d'Euclide etendu
    Par YASIR dans le forum Débuter
    Réponses: 5
    Dernier message: 09/04/2008, 19h15

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