Fonction : où réinitialiser le compteur [Résolu]

Signaler
Messages postés
146
Date d'inscription
lundi 14 août 2017
Statut
Membre
Dernière intervention
4 mars 2021
-
Messages postés
146
Date d'inscription
lundi 14 août 2017
Statut
Membre
Dernière intervention
4 mars 2021
-
Bonjour (ou bonsoir...),

j'essaye d'écrire une fonction qui retourne un mot sélectionné au hasard dans une liste, sachant que ce mot doit être inclus dans un ensemble (renseigné en paramètre).

Par exemple, le mot "jour" est inclus dans l'ensemble "bonjour", donc c'est bon.
A contrario, le mot "zoo" n'est pas inclus dans le mot "bonjour", à cause de la lettre z.
Si le mot n'est pas inclus dans l'ensemble, alors on choisit un nouveau mot dans la liste.

Cependant, la fonction ne retourne pas un résultat correct, et je pense que cela vient de la condition ligne 23/24. J'aimerais que mon compteur nbOfDifferentChar (qui vérifie si le mot généré contient une lettre qui ne fait pas partie des lettres de l'ensemble) se remette à 0 lorsque j'incrémente iMot.

A quel moment suis-je censée remettre à 0 ma variable nbOfDifferentChar ?

Bonne nuit...


static String getRandomWord(String ensemble) throws FileNotFoundException {

        ArrayList<String> dico = generateDico();
        int randomIndex;

        boolean isIncluded = false;
        int nbOfDifferentChar = 0;

        do{
            // génère un index aléatoire pour choisir un mot au hasard dans le dico
            randomIndex = (int) (Math.random()*(dico.size()-1));
            String mot = dico.get(randomIndex);

            // vérifie que le mot généré est plus petit que l'ensemble dans lequel il est contenu
            if ((ensemble.length() == mot.length()) || ensemble.length()<mot.length()){
                continue;
            }

            // vérifie que les lettres du mot généré correspondent aux lettres de l'ensemble
            for (int iMot = 0; iMot < mot.length(); iMot++) {
                for (int iEnsemble = 0; iEnsemble < ensemble.length(); iEnsemble++) {
                    if(mot.charAt(iMot) != ensemble.charAt(iEnsemble)){
                        nbOfDifferentChar++;
                    }
                }
            }

            // si une des lettres du mot est différente des lettres de l'ensemble
            if (nbOfDifferentChar == ensemble.length()){
                continue; //retour en début de boucle pour générer un nouveau mot
            }

            isIncluded = true;
        } while (isIncluded == false);

        return dico.get(randomIndex);
    }

5 réponses

Messages postés
16266
Date d'inscription
samedi 31 mai 2008
Statut
Modérateur
Dernière intervention
7 mars 2021
2 803
Bonjour,

Tel que tu l'as imaginé, le programme ne fonctionnera pas, tu ne peux pas choisir un mot au hasard et espérer qu'il respecte les contraintes. Imaginons que ton ensemble en paramètre ne corresponde à aucun mot, cela signifie que ta boucle va tourner indéfiniment, en analysant plusieurs fois le même mot, voire tous les mots à terme, mais tu continueras quand même.

Il faut tout reprendre de zéro. Et c'est l'occasion de se lancer sur de la programmation objet, car l'exercice s'y prête bien.

Déjà, il faut prendre en considération ce que j'ai pu te dire dans ta discussion précédente, pour avoir un algorithme efficace : il faut travailler sur des lettres triées, cela évite d'avoir une double boucle for quand une seule suffit.

Exemple complet :
import java.util.Arrays;

public class MotDico {
    private final String mot;
    private String lettresTriees;

    public MotDico(String mot) {
        this.mot = mot;
    }

    public String getMot(){
        return mot;
    }

    public String getLettresTriees(){
        if (lettresTriees == null) {
            char[] lettres = mot.toCharArray();
            Arrays.sort(lettres);
            lettresTriees = new String(lettres);
        }
        return lettresTriees;
    }

    public int length(){
        return mot.length();
    }

    public boolean contains(MotDico mot2) {
        if (length() < mot2.length())
            return false;
        String lettres = getLettresTriees();
        String lettres2 = mot2.getLettresTriees();
        int i = 0, i2 = 0;
        while (i < length() && i2 < mot2.length()) {
            if (lettres.charAt(i) == lettres2.charAt(i2)) {
                i++;
                i2++;
            } else if (lettres.charAt(i) < lettres2.charAt(i2)) {
                i++;
            } else {
                return false;
            }
        }
        return i2 == mot2.length();
    }

    @Override
    public String toString(){
        return mot;
    }
}

import java.io.*;
import java.nio.file.*;
import java.util.*;

public class Dico {
    private static final Random random = new Random();
    private final List<MotDico> mots;

    public Dico(String fileName) {
        mots = new ArrayList<>();
        try {
            for (String line : Files.readAllLines(Paths.get(fileName))) {
                mots.add(new MotDico(line));
            }
        } catch (IOException e) {
            throw new UncheckedIOException("Impossible de lire " + fileName, e);
        }
    }

    public List<MotDico> getAllMots(){
        return Collections.unmodifiableList(mots);
    }

    public List<MotDico> getAllMotsAvec(String lettres) {
        MotDico ensemble = new MotDico(lettres);
        List<MotDico> result = new ArrayList<>();
        for (MotDico mot : mots) {
            if (ensemble.contains(mot)) {
                result.add(mot);
            }
        }
        return Collections.unmodifiableList(result);
    }

    public MotDico getRandomMotAvec(String lettres) {
        List<MotDico> liste = getAllMotsAvec(lettres);
        if (liste.isEmpty())
            return null;
        return liste.get(random.nextInt(liste.size()));
    }
}

public class TestDico {
    public static void main(String[] args) {
        MotDico jour = new MotDico("jour");
        System.out.println(jour.contains(jour)); // true

        MotDico bonjour = new MotDico("bonjour");
        System.out.println(bonjour.contains(jour)); // true
        System.out.println(jour.contains(bonjour)); // false

        MotDico matin = new MotDico("matin");
        System.out.println(jour.contains(matin)); // false
        System.out.println(matin.contains(jour)); // false

        Dico dico = new Dico("E:/mots.txt");
        System.out.println(dico.getAllMotsAvec("bonjour")); // bon, jour, bonjour, juron, ...
        System.out.println(dico.getRandomMotAvec("bonjour")); // brun
    }
}
Messages postés
146
Date d'inscription
lundi 14 août 2017
Statut
Membre
Dernière intervention
4 mars 2021
1
D'accord, merci. Par contre quand j'ai essayé TestDico, j'ai eu toutes ces erreurs:
Exception in thread "main" java.io.UncheckedIOException: Impossible de lire E:/mots.txt
 at com.company.Dico.<init>(Dico.java:20)
 at com.company.Dico.main(Dico.java:63)
Caused by: java.nio.file.NoSuchFileException: E:\mots.txt
 at java.base/sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:85)
 at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:103)
 at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:108)
 at java.base/sun.nio.fs.WindowsFileSystemProvider.newByteChannel(WindowsFileSystemProvider.java:235)
 at java.base/java.nio.file.Files.newByteChannel(Files.java:375)
 at java.base/java.nio.file.Files.newByteChannel(Files.java:426)
 at java.base/java.nio.file.spi.FileSystemProvider.newInputStream(FileSystemProvider.java:420)
 at java.base/java.nio.file.Files.newInputStream(Files.java:160)
 at java.base/java.nio.file.Files.newBufferedReader(Files.java:2916)
 at java.base/java.nio.file.Files.readAllLines(Files.java:3396)
 at java.base/java.nio.file.Files.readAllLines(Files.java:3436)
 at com.company.Dico.<init>(Dico.java:16)
 ... 1 more


pourtant j'ai bien mis le chemin exact où se trouve mon fichier avec
Dico dico = new Dico("C:\\Users\\Slash\\IdeaProjects\\2-somme\\src\\com\\company\\mots.txt");


dans la fonction de test, mais ça ne change rien...
Messages postés
16266
Date d'inscription
samedi 31 mai 2008
Statut
Modérateur
Dernière intervention
7 mars 2021
2 803
Le message d'erreur référence toujours "E:\mots.txt" donc c'est que la classe de test n'a pas été recompilé avant d'être exécuté, c'est toujours ma version du code qui est pris en compte.
Messages postés
146
Date d'inscription
lundi 14 août 2017
Statut
Membre
Dernière intervention
4 mars 2021
1
oui désolée, j'ai oublié de changer les erreurs en conséquence, du coup j'obtiens:

Exception in thread "main" java.io.UncheckedIOException: Impossible de lire C:\Users\Slash\IdeaProjects\2-somme\src\com\company\mots.txt
	at com.company.Dico.<init>(Dico.java:21)
	at com.company.Dico.main(Dico.java:71)
Caused by: java.nio.file.NoSuchFileException: C:\Users\Slash\IdeaProjects\2-somme\src\com\company\mots.txt
	at java.base/sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:85)
	at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:103)
	at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:108)
	at java.base/sun.nio.fs.WindowsFileSystemProvider.newByteChannel(WindowsFileSystemProvider.java:235)
	at java.base/java.nio.file.Files.newByteChannel(Files.java:375)
	at java.base/java.nio.file.Files.newByteChannel(Files.java:426)
	at java.base/java.nio.file.spi.FileSystemProvider.newInputStream(FileSystemProvider.java:420)
	at java.base/java.nio.file.Files.newInputStream(Files.java:160)
	at java.base/java.nio.file.Files.newBufferedReader(Files.java:2916)
	at java.base/java.nio.file.Files.readAllLines(Files.java:3396)
	at java.base/java.nio.file.Files.readAllLines(Files.java:3436)
	at com.company.Dico.<init>(Dico.java:17)
	... 1 more



je vois pas quoi faire de plus étant donné que le chemin est déjà correct...
Messages postés
16266
Date d'inscription
samedi 31 mai 2008
Statut
Modérateur
Dernière intervention
7 mars 2021
2 803
NoSuchFileException signifie que le fichier n'existe pas, c'est probablement le chemin d'accès qui est mal écrit. Tu peux essayer en copiant le fichier dans un autre dossier, par exemple C:/mots.txt
Messages postés
146
Date d'inscription
lundi 14 août 2017
Statut
Membre
Dernière intervention
4 mars 2021
1
Le nom du fichier n'était pas le bon, autant pour moi... et merci pour ta patience...

J'ai un peu modifié la fonction getRandomMotAvec, afin que le mot soit retourné seulement s'il est contenu dans le dictionnaire.

Par ailleurs, j'ai voulu utilisé cette méthode pour obtenir le complément d'un mot.
Par exemple, avec l'ensemble "bonjour", et le mot "jour", j'obtiens le complément "bno". Elle fonctionne.
static String getComplement(String ensemble, String mot){

        String complement = ensemble;

        for(char c:mot.toCharArray()){
            // supprime la lettre (dans ensemble) équivalente à la lettre c (dans mot)
            complement=complement.replaceFirst(""+c, "");
        }
        return complement;

    }


Cependant, j'ai essayé de transformer la méthode en non static avec

public String getComplement2(String ensemble, String mot){

        String complement = ensemble;

        for(char c:mot.toCharArray()){
            // supprime la lettre (dans ensemble) équivalente à la lettre c (dans mot)
            complement=complement.replaceFirst(""+c, "");
        }
        return complement;

    }


et en testant avec

String complement = complement.getComplement2();


mais ça ne marche pas parce que complement n'a pas été initialisé, mais c'est de cette manière que j'aimerais l'initaliser justement.
Qu'est-ce que je devrais modifier pour que la méthode marche en non static ?
Messages postés
16266
Date d'inscription
samedi 31 mai 2008
Statut
Modérateur
Dernière intervention
7 mars 2021
2 803
"J'ai un peu modifié la fonction getRandomMotAvec, afin que le mot soit retourné seulement s'il est contenu dans le dictionnaire."
Je ne comprends pas ce que tu veux dire, le mot retourné est forcément contenu dans le dictionnaire, d'où est-ce qu'il viendrait sinon ?

Quant à ton complément, on en revient à ce qui a déjà été dit, il faut travailler avec les char[] plutôt qu'avec des String, et avec les lettres triées c'est plus efficace, donc utiliser un MotDico plutôt qu'un String.

Dans la classe MotDico :
public String remove(MotDico mot2) {
    String lettres = getLettresTriees();
    String lettres2 = mot2.getLettresTriees();
    char[] result = new char[length()];
    int i = 0, i2 = 0, n = 0;
    while (i < length() && i2 < mot2.length()) {
        if (lettres.charAt(i) == lettres2.charAt(i2)) {
            i++;
            i2++;
        } else if (lettres.charAt(i) < lettres2.charAt(i2)) {
            result[n] = lettres.charAt(i);
            n++;
            i++;
        } else {
            i2++;
        }
    }
    while (i < length()) {
        result[n] = lettres.charAt(i);
        n++;
        i++;
    }
    return new String(Arrays.copyOf(result, n));
}

Ce qui donne dans TestDico :
System.out.println(bonjour.remove(jour)); // bno
System.out.println(bonjour.remove(matin)); // bjooru
System.out.println(matin.remove(bonjour)); // aimt
Messages postés
146
Date d'inscription
lundi 14 août 2017
Statut
Membre
Dernière intervention
4 mars 2021
1
Je pensais qu'il y avait des mots qui n'appartenaient pas au dictionnaire étant donné qu'il y avait des mots un peu curieux, comme "ru" ou "no", mais effectivement ils font bien partis du dictionnaire.

Pour la méthode remove, je viens d'essayer et ça marche bien effectivement, merci une fois de plus.

J'aimerais faire une méthode qui automatise tout de A à Z pour obtenir le complément d'un mot, c'est-à-dire que cette méthode prend tout en paramètre (dictionnaire, ensemble, mot) dès le début pour renvoyer directement le complément (au lieu de le faire "manuellement, étape par étape" en dehors d'une fonction).

Si je place cette méthode dans la classe MotDico, ça reste cohérent ? (Etant donné que ça va faire appel à une méthode de la classe Dico.)
Messages postés
16266
Date d'inscription
samedi 31 mai 2008
Statut
Modérateur
Dernière intervention
7 mars 2021
2 803
Non, la classe Dico manipule des MotDico, mais pas l'inverse.
Il vaut mieux faire ton code dans une classe à part, un peu comme le fait TestDico.
Messages postés
146
Date d'inscription
lundi 14 août 2017
Statut
Membre
Dernière intervention
4 mars 2021
1 >
Messages postés
16266
Date d'inscription
samedi 31 mai 2008
Statut
Modérateur
Dernière intervention
7 mars 2021

d'accord, je vais faire ça alors, merci pour ton aide une fois de plus !