Dictionnaire : je m'embrouille

#1

J’ai cette fonction qui me permet d’obtenir une clé au hasard du dictionnaire level :

func afficherImage() {
    
        if let elementAuHasardDuDictionnaire = level.randomElement()?.key {
            
            elementAffiche = level[elementAuHasardDuDictionnaire]!

            produitImageView.image = UIImage(named : elementAuHasardDuDictionnaire)
         
        }

Mais je n’arrive pas à sortir cette clé de cette fonction pour la réutiliser dans cette autre fonction :

 func verifierReponse() {
    if let reponse = reponseTextField.text {
        
        let reponseAttendue = String(elementAffiche)
        
        if reponse == reponseAttendue {
        //code permettant de retirer la bonne réponse du tableau
            
        //level = level.filter {$0.key != String(reponseAttendue)}
            
            level = level.filter {$0.value != Int(reponseAttendue)}

Merci pour votre aide.

#2

Je crois qu’il faut reprendre le problème à la base, sans penser “technologie et codage”.

Si je comprend bien tu veux réaliser un Quizz avec des questions aléatoires venant d’une liste. Chaque question posée doit être retiré de la liste.

Une question contient juste un nom d’image et un chiffre. Pas de texte ?

Si c’est bien le cas, l’utilisation d’un dictionnaire n’est pas utile. Un simple tableau peut faire l’affaire, de manière plus efficace.

Je te fais un brouillon technique demain.

1 Like
#3

Oui pas de texte, une image et un nombre.

#4

Première étape : créer une classe pour gérer les questions

class UneQuestion {
    var image = ""
    var numero = 0
    
    init(numero:Int, image:String) {
        self.image = image
        self.numero = numero
    }
    
}

Ensuite une classe pour gérer une liste de questions, encapsulant un tableau. J’appelle cela un Sac, parce que cela fonctionne exactement comme un sac de billes.

On peut :

  • ajouter une question

  • retirer une question aléatoire (elle est automatiquement effacée du sac)

  • connaître l’état du sac (plein ou vide)

  • connaître le nombre de questions disponibles

      enum EtatSacQuestions {
          case vide
          case plein
      }
    
      class SacDeQuestions {
          private var tableau = [UneQuestion]()
          
          // Lecture du nombre de questions disponibles
          func nombreDeQuestions() -> Int {
              return tableau.count
          }
          
          func etatDuSac() -> EtatSacQuestions {
              if tableau.count == 0 {
                  return .vide
              }
              return .plein
          }
          
          // Ajouter une question dans le sac
          func ajouter(question:UneQuestion) {
              tableau.append(question)
          }
          
          // Retirer une question du sac
          func retirerQuestion() -> UneQuestion? {
              
              // Vérification du nombre de questions disponibles
              let nbQuestions = nombreDeQuestions()
              // Si le sac est vide, on retourne nil
              if nbQuestions == 0 {
                  return nil
              }
              
              // Tirage index aléatoire
              let index = Int.random(in: 0...nbQuestions-1)
              // Lecture question
              let question = tableau[index]
              // On l'efface du Sac
              tableau.remove(at: index)
              
              return question
          }
      }
    

Exemple d’utilisation :

class ViewController: UIViewController {
    
    let sac = SacDeQuestions()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        creerQuestions()
        
        // affichage
        while sac.etatDuSac() == .plein {
            if let question = sac.retirerQuestion() {
                let numero = question.numero
                let image = question.image
                print ("numéro : ", numero, " image : ", image)
            }
        }
        print ("Fin des questions..")
        
    }

    func creerQuestions() {
        sac.ajouter(question: UneQuestion(numero: 12, image: "IMG1"))
        sac.ajouter(question: UneQuestion(numero: 7,  image: "IMG2"))
        sac.ajouter(question: UneQuestion(numero: 5,  image: "IMG3"))
        sac.ajouter(question: UneQuestion(numero: 18, image: "IMG4"))
    }

}

Test :

numéro : 7 image : IMG2

numéro : 18 image : IMG4

numéro : 12 image : IMG1

numéro : 5 image : IMG3

Fin des questions…

Tu peux créer plusieurs sacs pour les différents niveaux de difficulté.

Des questions ?

1 Like
#5

Merci beaucoup pour cette version experte et merci pour les commentaires intégrés.

Mais je suis loin de tout comprendre, je dois revoir les cours, ceux qui correspondent aux class notaM.

Les questions et les images correspondantes, elles sont stockées dans quelle partie du code ? dans la func creerQuestions() ?

Dans cette fonction, que veut dire le point dans .vide ? .plein ?

func etatDuSac() -> EtatSacQuestions {
      if tableau.count == 0 {
          return .vide
      }
      return .plein
  }
1 Like
#6

La nature des informations que l’on peut stocker dans une variable dépend de son type : des chaines pour une String, des nombres complexes dans un flottant, une information binaire (oui/non, true/false) dans une BOOL, etc …

L’opérateur enum (comme énumération) permet de créer un type de variable spécialisée ne pouvant contenir qu’un petit nombre d’informations.

C’est très pratique pour mémoriser l’état d’un système ou d’un objet. Exemple pour un objet voiture pouvant prendre 3 états différents :

  enum EtatVoiture {
      case auGarage
      case enPanne
      case surLaRoute
  }

Il suffit de créer une variable de type EtatVoiture pour stocker l’information.

var etatVehiculePapa:EtatVoiture = .auGarage

Cela permet d’écrire un code très lisible, comme :

if etatVehiculePapa == .enPanne {
}

On peut aussi utiliser des entiers pour mémoriser l’état d’un système, mais c’est loin d’être aussi lisible.

// La signification de ce test ne saute pas aux yeux
// à la différence du précédent
if etatVehiculePapa == 1 {
}

De plus, les types de variables personnalisés sont sécurisés, toujours la même obsession des créateurs de Swift. On ne peut y écrire dedans que les valeurs prévues à l’origine, à la différence des entiers qui peuvent contenir n’importe quoi.

Pour en revenir au Sac à questions, j’ai créé un type de variable pour décrire son état.

  enum EtatSacQuestions {
      case vide
      case plein
  }

Je trouve ça plus parlant qu’un true/false classique.

  func etatDuSac() -> EtatSacQuestions {
      if tableau.count == 0 {
          return .vide
      }
      return .plein
  }

La fonction etatDuSac() retourne la valeur .vide si le Sac est vide, et .plein s’il reste encore des questions.

En fait, l’écriture .vide est une abréviation de EtatDuSac.vide. Les premières versions de Swift nécessitaient d’écrire TypeDeVariable.valeur en entier. Heureusement, on peut maintenant se contenter de .valeur, Xcode analysant le contexte pour déterminer le type à prendre en compte.

.

1 Like
#7

Je ne comprend pas trop ta question. Les questions (numéro et nom des images) sont stockées dans des objets de type UneQuestion, qui sont eux-mêmes stockés dans un objet SacDeQuestion.

La fonction creerQuestions() sert à définir la valeur des paramètres des questions, mais c’est juste un exemple tapé à la vas-vite. On peut utiliser des tas de manières différentes pour définir les questions : une liste de tupple, un fichier de texte, un fichier XML, un outil graphique spécialisé, etc …

C’est clair. C’est la base. Tu codes selon la vieille méthode de grand-mère : la programmation structurée. C’était bien dans les années 70-80. Mais l’émergence de la programmation objet a tout changé. Il faut penser différemment, avec des objets communiquant les uns avec les autres, et non de simples lignes de code.

Ce n’est pas si simple que ça. Moi qui suis un autodidacte n’ayant jamais suivis de cours d’informatique, j’ai eu quelques difficultés à “penser objet”, même si maintenant je serais incapable de faire autrement.

#8

Citation Je ne comprend pas trop ta question. Les questions (numéro et nom des images) sont stockées dans des objets de type UneQuestion , qui sont eux-mêmes stockés dans un objet SacDeQuestion .

Dans ma version basique, le nom des images et les réponses (les questions et leur réponse) sont enregistrées au préalable dans un dictionnaire :

Dans cette version experte, où sont -elles stockées ? où les met-on ?

#9

Oui, dans la fonction creerQuestions(). Les fonctions trop longues sont illisibles et difficiles à comprendre/modifier. Il est préférable de diviser le code en une série de petites fonctions, remplissant chacune une tache bien précise.

#10

bonjour,
J’adores tes réponses elles sont très claire et précisent.
j’essaye de faire un quizz aussi mais en utilisant un UITextField pour la réponse qui doit être écrite par l’utilisateur, il y a beaucoup de similitude avec ce thème mais je vois que tu mentionne une différence au niveau de ce Quizz entre l’utilisation de nombre et de texte, je me demande pourquoi. Serai-ce parce que l’on utilise plutôt un dictionnaire + qu’un tableau ?
où parce que tu intègre des chaines de caractère au lieu d’entier ? pour la mise en place du code et des variables ?

#11

Bienvenu Vivien,

Dans le cas présent, la grosse différence entre les tableaux et les dictionnaires réside dans l’utilisation des optionals (? et !), un aspect de Swift que Fanfan semble mal maîtriser. Il aime bien placer des ! un peu partout dans son code, c’est Mal (et la principale cause de plantage dans les applications écrites en Swift).

C’est pourquoi je lui ai montré comment gérer son problème avec un tableau, sans passer par les optionals, ce qui simplifie le code.

Si j’étais un optimisateur fou, je te dirais aussi que l’accès à l’information stockée dans un tableau est beaucoup plus rapide qu’avec un dictionnaire (entre x3 à x10). Mais bon, les processeurs actuels sont tellement rapide que cela n’a guère d’importance, surtout sur de petites quantités de données.

1 Like
#12

Merci, sur mon projet j’ai du mal à faire le lien entre mon UITextField (là ou l’utilisateur entre la réponse) et la vérification de la réponse …
Pensez-vous qu’il faille stocker la réponse dans une variable puis la comparer à la reponse dans le sac ?

#13

Absolument. A chaque utilisation d’un composant graphique il faut systématiquement stocker l’information fournie par l’utilisateur dans une variable de l’application.

Mais attention à la saisie de texte. C’est facilement trompeur. L’utilisateur peut ajouter ou supprimer des majuscules, se tromper dans l’otograf des mots, ou encore se faire avoir par le correcteur orthographique d’iOS qui change parfois les mots de sa propre volonté.

Peux-tu nous montrer un exemple de tes questions ?

#14

Le principe de ce Quizz est de deviné le titre d’une chanson et en option (l’interprète ou le groupe) avec 3 indices qui se trouvent sur une images que j’ai monter au préalable. donc une image et 2 zone UITexteField

func creerDesQuestions() {

    sac.ajouter(question: Question(image3Indices: #imageLiteral(resourceName: "la place des grands hommes"), artistes: "Patrick Bruel", titreDesChanson: "Place des grands hommes", anneeDeLaChanson: 1989, jockerInstrumental: "", genreMusical: "variété française"))
#15

Outch … les risques d’erreur de frappe risquent d’être nombreux !

Je te conseille de faire des essais avec plusieurs personnes, pour voir si ton système n’est pas trop lourd à l’usage.

Si c’est le cas, tu pourrais envisager un mécanisme de quizz plus classique, en proposant de choisir parmi plusieurs réponses possibles. Par exemple, trois genres musicaux (le bon et deux autres tirés au hasard). Trois ou quatre artistes, etc …

#16

Oui mais j’ai limité la re correction automatique du clavier dans un premier temps, ainsi que la fonction native de propositions de mots. Ce serait un peu le même principe que l’application 94% je sais pas si tu connais ? Nous avons fait des phases de test avec une maquette sur les personnes autour de nous le principe semble plaire , j’aimerai proposer une expérience nouvelle sur ce genre de quizz

#17

Donc si je réponds : “La plasse des grand homme” il valide ma réponse ?

#18

Je ne connais pas. Je le télécharge ce soir, pour voir.

#19

C’est une application de jeu où une partie est un peu genre “La Famille en Or” (référence de vieux !) où il faut donner 94% des réponses que les gens ont donné.
Du genre, “Cites moi des animaux de la jungle” et tu dois donner, avec un nombre de réponses limitées, les réponses qui correspondent à 94% de ce que les gens ont répondu.

#20

Je suis vieux, ça tombe bien. Par contre, je ne regardais jamais les jeux tv (#MoiJeSuisUnIntelloJeregardeArteLaChaîneHistoireEtDoctorWho). Mais je comprend le principe. Merci de l’explication.