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

Scheme Discussion :

Structurer son 1er programme Scheme


Sujet :

Scheme

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Juillet 2013
    Messages
    18
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2013
    Messages : 18
    Points : 10
    Points
    10
    Par défaut Structurer son 1er programme Scheme
    Hello,
    Je débute en Scheme, et pour mon premier programme -un 'MasterMind'- je n'arrive pas à trouver la bonne structure, le bon découpage - aussi au niveau de l'indentation.
    J'ai essayé plein de commandes mais je suis à peu près que se sont les moins pertinents .
    Je n'arrive pas non plus à comprendre la différence entre 'and' et 'and?', alors que se sont tous les 2 des prédicats, je crois ?
    Et de même pour 'not' et 'not?' ?

    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
    #lang racket
    ;; Master Mind ;;
    (define nC 8)
    (define lType [list nC nC nC nC])
    (define nMyst (map (lambda (n) (random n)) lType))
    (define (ask)
       (let* ([i (read [current-input-port])])
         (if (and (integer? i)
                  (>= i 0)
                  (< i nC)) i (begin [printf "You must write a number between 0 and ~a\n" (- nC 1)]
                                     (ask)))
        )
       )
    (define (L L1)
      (let* ([ans (list (ask) (ask) (ask) (ask))])
        (list
         (map [lambda (a b) (if (equal? a b) 'T [if [ormap equal? (list a a a a) L1] 'B 'F])] ans L1)
         ans)
        )
      )
    (do ([nT 0 (+ nT 1)]
         [iList (L nMyst) (append (L nMyst) iList)])
      ((equal? (second iList) nMyst) (printf "You win: ~a !!" nMyst))
      (begin
        (printf "Round n°~a, \nF => False \nB => Bad Position \nT => True \nIt is incorrect: \n" nT)
        (for-each (lambda (arg) (printf "~a\n" arg)) iList)
        )
      )
    Merci pour votre aide !

  2. #2
    Membre actif
    Homme Profil pro
    Inscrit en
    Mai 2013
    Messages
    152
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations forums :
    Inscription : Mai 2013
    Messages : 152
    Points : 275
    Points
    275
    Par défaut
    Ce programme, qu'est-ce qu'il doit faire exactement ? Quel est l'objectif ?

    Pour indenter le code, le plus important c'est d'utiliser le bon éditeur. DrRacket n'est très avancé, mais au moins il indente proprement. Il ne faut pas laisser une seule parenthèse dans une ligne. Il est plus facile de lire un if si l'on commence une nouvelle ligne pour chaque branche. Il vaut la peine commencer une nouvelle ligne après les macros comme begin aussi, ainsi
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     (begin
       (printf ...)
       ...)
    and? et not? ne font pas partie de la Scheme ordinaire, est-ce que tu es sûr que tu en as besoin ?

  3. #3
    Membre actif
    Homme Profil pro
    Inscrit en
    Mai 2013
    Messages
    152
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations forums :
    Inscription : Mai 2013
    Messages : 152
    Points : 275
    Points
    275
    Par défaut
    C'est un bon guide à l'indentation (en anglais) :
    http://dept-info.labri.fr/~strandh/T...dentation.html
    Il s'agit de Common Lisp, mais ce n'est pas important.

  4. #4
    Membre à l'essai
    Profil pro
    Inscrit en
    Juillet 2013
    Messages
    18
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2013
    Messages : 18
    Points : 10
    Points
    10
    Par défaut
    Merci d'avoir répondu si vite et 2x qui plus est .

    En fait, le programme choisi 4 chiffres 'Mystères' puis on rentre 4 chiffres, et il compare ces chiffres rentrés aux chiffres 'mystères' puis dit pour chaque chiffres entrés s'il est à la bonne ou mauvaise position ou inexistant par rapport aux chiffres 'mystères'. Et continue ainsi tant que les bons chiffres n'ont pas étés trouvés.
    L'objectif était de tester le langage.

    Comment choisir entre utiliser une boucle 'do' ou une fonction récursive ?

    J'ai ré-indenté conformément -j'espère?- au document:
    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
    
    #lang racket
    ;; Master Mind ;;
    (define nC 8)
    (define lType [list nC nC nC nC])
    (define nMyst (map (lambda (n) (random n)) lType))
    (define (ask)
      (begin
        [printf "You must write a number between 0 and ~a:\n" (- nC 1)]
        (let* ([i (read [current-input-port])])
          (if (and (integer? i) (>= i 0) (< i nC))
              i
              (ask)))))
    (define (L L1)
      (let* ([ans (list (ask) (ask) (ask) (ask))])
        (list
         (map [lambda (a b) (if (equal? a b) 'T [if [ormap equal? (list a a a a) L1] 'B 'F])] ans L1)
         ans)))
    (do ([nT 0 (+ nT 1)]
         [iList (L nMyst) (append (L nMyst) iList)])
      ((equal? (second iList) nMyst) (printf "You win: ~a !!" nMyst))
      (begin
        (printf "Round n°~a, \nF => False \nB => Bad Position \nT => True \nIt is incorrect: \n" nT)
        (for-each (lambda (arg) (printf "~a\n" arg)) iList)))
    Si j'ai bien compris les prédicats, se sont des fonctions qui retournent un boolean et qui sont différenciées avec '?' à la fin, et donc pourquoi 'not' et 'and' n'ont pas de '?' à leur fins ?

    Je dérive un peu, mais est-il possible d'utiliser différent 'langage' racket dans un même programme ?

  5. #5
    Membre actif
    Homme Profil pro
    Inscrit en
    Mai 2013
    Messages
    152
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations forums :
    Inscription : Mai 2013
    Messages : 152
    Points : 275
    Points
    275
    Par défaut
    Je suppose qu'il est possible d'utiliser langages differents dans modules, mais je ne connais racket bien, il faut voire la documentation.

    Le corps d'une fonction peut être constitué par plusieures formes, alors ce n'est pas indispensable de l'envelopper dans un begin.

    Quant'au do, il n'est presque jamais utilisé en Scheme. On préfère la récursion, souvent en forme d'un « let nommé ».

    On préfère let au let* si l'on n'ont pas besoin de définitions séquentielles.

    L'essence du jeu peut être décomposée en trois parties: 1) la préparation (la génération d'une liste aléatoire), 2) l'entrée d'une réponse; 3) la comparaison de la réponse avec le clé. Ces parties sont indépendentes, alors il vaut la peine de les réaliser d'une façon indépendente.

    C'est la comparaison qui est le noyau du jeu. Etant données deux listes, c'est un algorithme déterministe. Donc, il peut être réalisé comme une fonction pure, c'est-à-dire, une fonction en sens mathématique. C'est bien parce qu'il est plus facile d'écrire telles fonctions et de les tester aussi.

    Je suppose que ta fonction L fait la comparaison. Son défaut principale est le fait qu'elle combine la comparaison avec l'entrée des données et avec l'interface de l'utilisateur.

    C'est une bonne idée d'utiliser un map. Au lieux d'un if intérieur il vaut mieux employer un cond. Cet ormap équivaut à la fonction member. Pour comparer les nombres, on utilise =. Alors, on peut écrire cette fonction comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    (define (compare answer key)
      (map (lambda (a k)
             (cond ((= a k) 't)
                   ((member a key) 'b)
                   (else 'f)))
           answer
           key))
    Maintenant on peut charger cette fonction en lisp et la tester dans le REPL.

    Pour générer le clé aléatoire, on peut utiliser une fonction qui accepte le nombre totale des chiffres et la limite supérieure. C'est raisonnable de définir les paramètres du jeu comme variables globales, mais cette fonction sera plus simple se elle ne les accédera. Il est plus facile de gérer une définition que trois en même temps.

    C'est un peu différente de ce que tu as proposé, mais ton (list nC nC nC nC) ne me dit pas du tout.

    On peut définir cette fonction de cette façon simple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    (define (random-list length limit)
      (if (zero? length)
          '()
          (cons (random limit) (random-list (- length 1)))))
    Mais ce n'est pas efficace, parse que la récursion n'est pas terminale. Voici la récursion terminale avec un « let nommé »:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    (define (random-list length limit)
      (let loop ((i 0)
                 (res '()))
        (if (= i length)
            res
            (loop (+ i 1) (cons (random limit) res)))))
    Ça ressemble bien a une boucle.

    Ta ask est une fonction qui valide l'entrée. Je pense qu'il est plus naturel de lui fournir la limite et même le méssage comme arguments, alors on obtient une fonction get-number assez générale. (current-input-port) c'est le port par défaut pour read, alors il ne vaut pas la peine l'indiquer.

    Pour obtenir une réponse de l'utilisateur, on peut écrire une fonction get-answer en se servant de random-list comme modèle.

    Le squelette de la fonction game est comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    (define (game)
      (let ((key (random-list game-length game-limit)))
        (let game-loop ((round 1))
          (report-round round)
          (let* ((answer (get-answer game-length game-limit))
                 (result (compare (get-answer game-length game-limit) key))
            (cond ((win? result) (display "Congrats!"))
                  (else (report-result answer result)
                        (game-loop (+ round 1)))))))))
    Lorsque tout ça fonctionne, on pouvait évoquer la fonction game dans le fichier :
    Mais ce doit être possible avoir un fichier de définitions et d'évoquer la fonction d'autre façon : par exemple, en indiquant la forme à évaluer à l'intérpreteur.

  6. #6
    Membre à l'essai
    Profil pro
    Inscrit en
    Juillet 2013
    Messages
    18
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2013
    Messages : 18
    Points : 10
    Points
    10
    Par défaut
    Merci beaucoup, j'ai entièrement refait le code selon le bon découpage et je suis vraiment content du résultat !

    Le système de récurrence et plutôt dur à appréhender pour un débutant, mais à force, ça rentre.

    Une question -la dernière, c'est promis - j'ai vu dans les différents testes qu’apparemment, le racket serait un langage très lent, par rapport aux autres langages, et même par rapport aux autres lisp, vérité ?, est-ce que tout les lisp sont lent ?, existe-t-il un lisp avec un ide abordable d'une bonne rapidité ?

    Le jeu refait avec le bon format et le bon découpage:
    (Il persiste juste un tout petit problème à la fin, quand on gagne le jeu, il y a un bug, il semblerait que le retour de la fonction principale soit incorrect ?!?)
    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
    #lang racket
    (define (random-list length limit)
      (let loop ((i 0)
                 (res '()))
        (if (= i length)
            res
            (loop (+ i 1) (cons (random limit) res)))))
    
    (define (compare answer key)
      (map (lambda (a k)
             (cond ((= a k) 'T)
                   ((member a key) 'B)
                   (else 'F)))
           answer
           key))
    
    (define (get-answer length limit)
      (let loop ((n 1))
        (begin
          (printf "You must write a number between 0 and ~a:\n" (- limit 1))
          (let* ([i (read)])
            (if (and (integer? i) (>= i 0) (< i limit))
                (cons i (if (= n length)
                            '()
                            (loop (+ n 1))))
                (loop n))))))
    
    (define (report-result historique)
      (map (lambda (a) (printf "~a\n" a))
           historique))
    
    (define (game game-length game-limit)
      (let* ((key (random-list game-length game-limit)))
        (let game-loop ((round 1)
                        (historique '("Résultats:")))
          (begin
            (printf "Tour n°~a:\n" round)
            (let* ((answer (get-answer game-length game-limit))
                   (result (compare answer key))
                   (historique (append historique (list result answer))))
              (if (equal? answer key)
                  (printf "Congrats: ~a !" key)
                  ((report-result historique)
                   (game-loop (+ round 1)
                              historique))))))))
    
    (game 4 8)

  7. #7
    Membre actif
    Homme Profil pro
    Inscrit en
    Mai 2013
    Messages
    152
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations forums :
    Inscription : Mai 2013
    Messages : 152
    Points : 275
    Points
    275
    Par défaut
    Ça paraît bien!

    Je ne suis pas sûr non plus à propos de ce bug, mais sans le point d'exclamation il ne se manifeste pas.

    Le corps d'un let peut être composé de plusieures formes, alors begin n'est pas indispensable dans get-answer.

    En plus, la récursion dans ton get-answer n'est pas terminale, parce que tu appelles cons après loop. Ça fait peu de différence dans une fonction qui obtient des données de l'utilisatuer, mais voici en une version avec récursion terminale, pour comparér :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    (define (get-answer length limit)
      (define (valid? i)
        (and (integer? i) (>= i 0) (< i limit)))
      (let loop ((n 0) (answer '()))
        (printf "You must write a number between 0 and ~a:\n" (- limit 1))
        (if (= n length)
    	answer
    	(let ((i (read)))
    	  (if (valid? i)
    	      (loop (+ n 1) (cons i answer))
    	      (loop n answer))))))
    Dans la programmation impérative on distinge entre boucles et récursion, alors que en Scheme il faut bien distinguer entre la récursion générale et terminale, parce que ce n'est que cette dernière qui peut être optimisée et qui est un analogue de la boucle. Ou bien en une généralisation, car il est facile de transformer une boucle dans une récursion terminale. Les paramètres de la boucle, telles comme compteurs et accumulateurs, deviennent paramètres de la fonction récursive.

    Quant'à la rapidité, franchement, je ne sais pas. Je suis amateur, et je code principalement en Common Lisp. Je pensais que Chicken Scheme, par exemple, n'est pas mauvaise, et je suis même surpris que Racket est si inefficace. Quant'au Common Lisp, SBCL est généralement consideré rapide, et Clozure Common Lisp est bon aussi.

    L'IDE c'est toujours Emacs. Slime pour Common Lisp est parfait, il y a aussi analogues pour certaines Schemes.

    la dernière, c'est promis
    C'est dommage, ces sections ne sont pas exactement animées.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 0
    Dernier message: 30/12/2010, 22h28
  2. mieux structurer son programme C++ ?
    Par Sceener dans le forum C++
    Réponses: 12
    Dernier message: 01/07/2007, 13h53
  3. [debutant] structure d'un programme
    Par poukill dans le forum Débuter
    Réponses: 17
    Dernier message: 19/05/2006, 15h33
  4. Debutant en JAVA, problème avec 1er programme
    Par Gymerus dans le forum Entrée/Sortie
    Réponses: 13
    Dernier message: 07/09/2005, 12h10
  5. Enregistrement du son par programme
    Par Invité dans le forum C++Builder
    Réponses: 3
    Dernier message: 10/06/2003, 23h13

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