Falscher Rückgabewert

kodela

Bekanntes Mitglied
Hallo,

zur Überprüfung, ob eine Stradoku-Aufgabe nur eine Lösung hat, verwende ich folgende Methode:
Java:
    /**
     * Überprüft nach der Neueingabe oder Bearbeitung eines Stradoku,
     * ob und mit welchem Level die Aufgabe gelöst werden kann.
     * @param tstEindeutigkeit Flag für Eindeutigkeitsprüfung
     * @param check16 Flag für Prüfung auf Archivtauglichkeit
     * @return eindeutig gelöst:                    Level (1-5),
     *         nicht eindeutig gelöst:              0
     *         nicht gelöst, also fehlerhaft:       -1
     *         bei Prüfung auf Archivtauglichkeit:  nicht tauglich Level > 9,  tauglich 1 - 5
     */
    public int loeseStradoku(boolean tstEindeutigkeit, boolean check16) {
        level = -1;
        filterKnd = 0;
        if (getVorgabewerte() > 0 || getSperrzellen() > 0) {
            System.arraycopy(aufgabe, 0, stradoku, 0, 81);
            erstelleKndListe(stradoku);
            for (int i = 0; i < 81; i++) {
                loesung[i] = 0;
            }
            if (check16) {
                ArchivLevel aLevel = new ArchivLevel();
                level = aLevel.getArchivLevel(stradoku, loesung);
            } else {
                strLoeser = new LevelLoeser(this, stradoku, loesung, true);
                level = strLoeser.loeseAufgabe();
            }
            // level 1 bis 4 per se eindeutig
            if (level == 5 && tstEindeutigkeit) {
                eindeutig = false;
                HinweisWarten hnw = new HinweisWarten(strApp);
                EindeutigkeitChecken echeck = new EindeutigkeitChecken(
                        strApp, hnw, stradoku, loesung);
                echeck.execute();
                eindeutig = echeck.is_eindeutig();
                if (!eindeutig) {
                    level = 0;
                }
            }
        }
        return level;
    }

Bei HinweisWarten handelt es sich um eine simple Dialogklasse mit eine progressiven Fortschrittsanzeige.

Die Klasse EindeutigkeitChecken prüft, ob eine Stradoku-Aufgabe eindeutig lösbar ist. Hier der Code dieser Klasse:
Java:
import javax.swing.SwingWorker;
import static stradoku.GlobaleObjekte.LWF;
import static stradoku.GlobaleObjekte.LWRT;
import static stradoku.GlobaleObjekte.kopieren;

/**
* Überprüft Stradoku auf Eindeutigkeit
* @author Konrad
*/
public class EindeutigkeitChecken extends SwingWorker<Boolean, Object>
                                implements GlobaleObjekte {
    private LevelLoeser strLoeser;
    private final int[] aufgabe;
    private final int[] loesung;
    private boolean eindeutig;
    HinweisWarten hnwDialog;
   
    /**
     * Konstruktor
     * @param mf    Referenz auf Hauptklasse der Anwendung
     * @param hnw   Referenz auf Warten-Dialog
     * @param af    Bitcodierte Stradoku Aufgabe
     * @param ls    Bitcodierte Stradoku Lösung
     */
    EindeutigkeitChecken(StradokuApp mf, HinweisWarten hnw, int[] af, int[] ls) {
        aufgabe = new int[81];
        kopieren(af, aufgabe, true);
        loesung = new int[81];
        kopieren(ls, loesung, false);
        eindeutig = true;
        if (hnw != null){
            hnwDialog = hnw;
            hnwDialog.zeigeHinweis("<html><center><b>Eindeutigkeit wird überprüft."
                    + "<br><br>Bitte solange warten.</b></center></html>");
        }
    }
   
    /**
     * Überschreibt die entsprechende Methode der Superklasse SwingWorker
     * und führt im Hintergrund die Eindeutigkeitsprüfung für ein Stradoku aus.
     * Sie steuert außerdem ein Fortschrittsanzeige.
     * @return  true wenn eindeutig, sonst false
     */
    @Override
    public Boolean doInBackground() {
        System.out.print("\nCheck wird durchgeführt.\n");
        int afg[] = new int[81];
        int lsg[] = new int[81];
        kopieren(aufgabe, afg, false);
        for (int i = 0; i < 81; i++) {
            System.out.printf("  Zelle %d\n", i);
            hnwDialog.setVortschritt((int)(i*1.25)+2);
            int lw = loesung[i] & ~LWF;
            if (loesung[i] <= LWRT && loesung[i] > 9) {
                for (int k = 1; k <= 9; k++) {
                    if (lw != k) {
                        System.out.printf("    Kandidat %d Lösungswert %d\n", k, lw);
                        afg[i] = k;
                        strLoeser = new LevelLoeser(null, afg, lsg, false);
                        int lev = strLoeser.loeseAufgabe();
                        if (lev > 0) {
                            eindeutig = false;
                            System.out.print("Zweite Lösung gefunden. Check wird mit 'false' beendet.\n");
                            return false;
                        } else {
                            kopieren(aufgabe, afg, false);
                        }
                    }
                }
            }
        }
        eindeutig = true;
        System.out.print("Check wird mit 'true' beendet.\n");
        return true;
    }  
   
    /**
     * Überprüft für jede zu lösende Zelle, ob das Stradoku
     * mit mehr als einem Wert gelöst werden kann.
     * @return  true wenneindeutig, sonst false
     */
    public Boolean checkEindeutigkeit() {
        int afg[] = new int[81];
        int lsg[] = new int[81];
        kopieren(aufgabe, afg, false);
        for (int i = 0; i < 81; i++) {
            int lw = loesung[i] & ~LWF;
            if (loesung[i] <= LWRT && loesung[i] > 9) {
                for (int k = 1; k <= 9; k++) {
                    if (lw != k) {
                        afg[i] = k;
                        strLoeser = new LevelLoeser(null, afg, lsg, false);
                        int lev = strLoeser.loeseAufgabe();
                        if (lev > 0) {
                            return false;
                        } else {
                            kopieren(aufgabe, afg, false);
                        }
                    }
                }
            }
        }
        return true;
    }  
   
    /**
     * Beantwortet für das überprüfte Stradoku die Frage auf Eindeutigkeit.
     * @return true wenn eindeutig, sonst false
     */
    public boolean is_eindeutig() {
        hnwDialog.setVisible(false);
        return eindeutig;
    }
}


Zur Überprüfung, ob die Klasse auch korrekt arbeitet, habe ich eine Reihe von Ausgaben eingebaut. Für ein simples Beispiel sieht die erzeugte Testausgabe so aus:
Code:
Check wird durchgeführt.
  Zelle 0
    Kandidat 2 Lösungswert 1
Zweite Lösung gefunden. Check wird mit 'false' beendet.

Trotzdem gibt die Methode 'true' zurück. Was mache ich falsch, kann mir jemand bitte helfen?
 

kodela

Bekanntes Mitglied
Die gibt false zurück, woher weißt du, dass sie true zurück geben soll?
Ich habe doch geschrieben, die Methode gäbe 'true' zurück. Das ist immer so und im konkreten Beispiel falsch, denn die Auswertung hat eine zweite Lösung gefunden. In diesem Fall müsste 'false" zurückgegeben werden.

Ich gehe davon aus, dass die Abfrage von is_eindeutig() beantwortet wird, bevor die Überprüfung beendet ist. Das kann ich sehen, wenn ich ein Stradoku teste, bei dem alle 81 Zellen überprüft werden müssen, da wird das Ergebnis der Abfrage von is_eindeutig() bereits angezeigt, während meine System.out.printf()-Ausgaben immer noch laufen. Das sieht dann für die letzte Zelle so aus:
Code:
  Zelle 80
    Kandidat 1 Lösungswert 3
    Kandidat 2 Lösungswert 3
    Kandidat 4 Lösungswert 3
    Kandidat 5 Lösungswert 3
    Kandidat 6 Lösungswert 3
    Kandidat 7 Lösungswert 3
    Kandidat 8 Lösungswert 3
    Kandidat 9 Lösungswert 3
Check wird mit 'true' beendet.
Es wurden hier für alle Zellen alle eventuell möglichen Kandidaten getestet und nur mit dem Lösungswert 3 ließ sich das Stradoku lösen.
 
Zuletzt bearbeitet:

kodela

Bekanntes Mitglied
Wenn ich in der Klasse EindeutigkeitChecken am Ende folgende Änderung vornehme, dann ist das Ergebnis richtig, allerdings steht bis zum Ergebnis und das kann auch einmal eine halbe Minute dauern, alles still ohne dass der Warten-Dialog mit der Fortschrittsanzeige zu sehen ist:
Java:
    public boolean is_eindeutig() {
        while (isDone() == false) {
        }
        hnwDialog.setVisible(false);
        return eindeutig;
    }
    
    @Override
    protected void done() {
    }

PS: Ich weiß, dass man Fortschritt nicht mit 'V' schreibt und trotzdem ist es mir im Code für doInBackground() passiert. - Großes Schämen!
 

LimDul

Top Contributor
Ah, jetzt kommt Licht in die Sache. Du hättest im ersten Beitrag erwähnen sollen, um welche Codestelle es geht.

Nachdem du execute aufgerufen hast, sollte in der Regel deine Methode enden. Du kannst nicht danach auf das Ergebnis zugreifen. Denn das wird asynchron berechnet. Darauf warten ist - wie du festgestellt hast - ebenfalls falsch. Dann blockierst du die Gui. Der Code, der danach aufgerufen werden soll, muss von der Methode done aufgerufen werden.
 

kodela

Bekanntes Mitglied
Nachdem du execute aufgerufen hast, sollte in der Regel deine Methode enden. Du kannst nicht danach auf das Ergebnis zugreifen. Denn das wird asynchron berechnet. Darauf warten ist - wie du festgestellt hast - ebenfalls falsch. Dann blockierst du die Gui. Der Code, der danach aufgerufen werden soll, muss von der Methode done aufgerufen werden.
Danke! Nach dem Aufruf von execute die aufrufende Methode zu beenden, ist leicht gesagt, aber damit ist mein Problem nicht behoben, denn es gibt ja in den meisten Fällen eine Methode, die auf das Ergebnis wartet.

Ich will einmal den Ablauf für den Regelfall grob skizzieren:

Der Anwender kann eine Stradoku-Aufgabe neu eingeben, selbst eine solche kreieren oder die aktuelle Aufgabe bearbeiten, also in irgend einer Weise verändern. Soll dieser Vorgang beendet werden, muss geprüft werden, ob die Aufgabe lösbar ist, wenn ja, welchen Level sie hat und wenn der Level größer 4 ist, dann muss auch noch geprüft werden, ob sie eindeutig lösbar ist. Für die Levels 1 bis 4 steht die Eindeutigkeit allein dadurch fest, dass dieser Level bestimmt werden konnte.

Diese Überprüfung wird von der Methode loeseStradoku() übernommen. Sie veranlasst zunächst einmal den Aufruf der Methode loeseAufgabe(). Diese gibt den Level zurück, der dann an die Bearbeitungsmethode weitergegeben wird. Gibt loeseAufgabe() den Level 5 zürückm muss zuvor jedoch noch geprüft werden, ob die Aufgabe eindeutig lösbar ist, wenn nicht, dann wird der Level 5 zu Level 0 geändert. Die bearbeitende Methode weiß jetzt Bescheid und kann dem Anwender angepasste Möglichkeiten zur Entscheidung über das weitere Vorgehen bieten. Ist eine Aufgabe zum Beispiel nicht lösbar (Level -1), kann er sich zum Beispiel entscheiden, ob er die Bearbeitung fortsetzen oder abbrechen will. Das Ergebnis wird also nicht übernommen. Ähnlich ist es mit den anderen Levels.

Die Überprüfung der Eindeutigkeit muss aber auch erfolgen, wenn eine Stradoku-Aufgabe importiert wird, zum Beispiel über die Zwischenablage oder über eine externe Liste und schließlich auch, wenn der Anwender die Überprüfung über eine entsprechende Menüoption vornehmen will.

Es gibt also eine Reihe von Punkten, von denen aus die Eindeutigkeitsprüfung veranlasst werden kann und die alle auf das Ergebnis warten, um auf die eine oder andere Weise darauf zu reagieren. Bei der jetzigen Lösung gibt die Methode loeseStradoku() das Ergebnis an die aufrufende Methode weiter, egal welche dies ist. Was passiert aber, wenn ich loeseStradoku() nach dem Aufruf von execute() beende.
 

LimDul

Top Contributor
Du redest von Methoden - das ist das falsche Denken. Du musst eher in Zuständen denken.

Du startest den SwingWorker. Dann geht deine Gui in den Zustand "Aufgabe wird im Hintergrund gelöst". Das heißt, das gewisse Eingabefelder etc. gesperrt werden usw. Aber die Methode ist dann fertig.

Wenn der SwingWorker fertig ist, signalisiert er das über die done() Methode, die dann in der Gui den Zustand setzt "Lösung geprüft". Und dann machst du die Dinge, die dann passieren sollen.

Sprich, alles was nach dem execute passiert muss in eigene Methode, die vom done() vom SwingWorker aufgerufen wird. GUI-Programmierung ist in der Regel nicht linear, dass du alles hintereinander tust, sondern ereignisgesteuert.
 

kodela

Bekanntes Mitglied
Du redest von Methoden - das ist das falsche Denken. Du musst eher in Zuständen denken.
Ja, das ist mir klar, aber im konkreten Fall hilft mir das nicht weiter. Der von Dir beschriebene Ablauf entspricht bei mir zum Beispiel genau meinem Vorgehen für den Ausdruck von Stradokus. Das ist aber eine ganz andere Situation, als die Eindeutigkeitsüberprüfung. Während der Initialisierung des Druckers muss abgewartet werden, so lange wird der Warten-Dialog angezeigt, Danach kann sich der Anwender mit beliebigen anderen Aktivitäten beschäftigen, denn der Ausdruck selbst erfolgt im Hintergrund.

Will der Anwender aber zum Beispiel ein neues Stradoku eingeben, dann müssen vor einer Übernahme einige Dinge abgeklärt werden. Dazu gehört in bestimmten Fällen auch die Eindeutigkeitsprüfung, von der es abhängt, welche Möglichkeiten dem Anwender für das weitere Vorgehen angeboten werden können. Mit dem Aufruf der Eindeutigkeitsprüfung ist die Suppe also noch lange nicht gegessen, ganz anders als beim Ausdruck, da folgt nach der Druckerinitialisierung ganz einfach der Ausdruck selbst.
 

httpdigest

Top Contributor
Das steht ja nicht entgegen dem, was @LimDul sagte. Er erklärt dir die ganze Zeit auf technischer Ebene, wie ereignisgetriebene GUI-Programmierung funktioniert und du erklärst die ganze Zeit auf fachlicher Ebene, was deine Anwendung tun soll.
Letzteres hat aber jetzt schon jeder verstanden, denke ich.
Was einfach nur wichtig ist, auf technischer Ebene zu realisieren: Wenn dein SwingWorker fertig ist, dann musst du in dessen done()-Methode (wie @LimDul ja schon sagte) darauf reagieren und dann tun, was dann getan werden soll.
Wenn das, was nach der Eindeutigkeitsprüfung geschehen soll, wie du ja schon fachlich beschrieben hast, davon abhängt, von wo aus die Prüfung angestossen wurde, dann musst du das eben in dem SwingWorker parametrisieren. Z.B. durch ein einfaches Runnable.
Also nicht die done()-Methode des SwingWorkers hartcoden, sondern einfach bei der Erzeugung des SwingWorkers reingeben, was denn passieren soll, wenn er mit seiner Berechnung fertig ist.
IN JEDEM FALL musst du aber synchron zum SwingWorker in dessen don()-Methode (also asynchron zum Code, der die Ausführung des SwingWorker angestossen hat) jede weitere Aktion anstossen, um eben nicht die GUI zu blockieren.
Du musst also die GUI in einen Zustand versetzen, der es dem Nutzer erstmal nicht erlaubt "weiterzumachen", solange die Prüfung nicht abgeschlossen ist.
 

kodela

Bekanntes Mitglied
Danke für die Hinweise!

Nun ist mir klar, dass es offensichtlich kein "Hintertürchen" gibt, über das ich mein Vorhaben mit der Vortschrittsanzeige über den SwingWorker ohne riesigen Aufwand erreichen kann. Die Eindeutigkeitsprüfung muss in einer Vielzahl von Fälle eventuell aufgerufen werden, zum Beispiel bei der Übernahme eines Stradoku aus der Zwischenablage oder aus einer Liste, bei Neueingaben, bei der Generierung und im Zusammenhang mit der Modifizierung eines Stradokus. Darauf immer richtig zu reagieren wäre zwar sicher möglich, aber der Aufwand dafür ist mir die Fortschrittsanzeige nicht wert.

Ich habe jetzt daher auf die Fortschrittsanzeige verzichtet und es funktioniert. Einziges Problem: Der Hinweis-Dialog wird angezeigt und beendet, aber ohne den Text. Warum dieser nicht angezeigt wird, ist mir ein Rätsel.

Der Code für die Erzeugung des Dialoges sieht so aus:
Code:
HinweisWarten hnw = new HinweisWarten(strApp);
hnw.setModal(false);
hnw.zeigeHinweis("<html><center><b>Eindeutigkeit wird überprüft."
+ "<br><br>Bitte solange warten.</b></center></html>");
hnw.setVisible(true);

Ich hoffe, dieses Problem auch noch lösen zu können. Vielleicht kann mir jemand einen Tipp geben.

Für Eure (@LimDul und @mihe7) Hilfe bedanke ich mich vielmals.

kodela
 

kodela

Bekanntes Mitglied
Hallo,

mein Problem ist gelöst und es war einfacher, als man dachte. Hier die Methode doInBackground() der Klasse EindeutigkeitChecken:
Java:
    public Boolean doInBackground() {
        int afg[] = new int[81];
        int lsg[] = new int[81];
        kopieren(aufgabe, afg, false);
        for (int i = 0; i < 81; i++) {
            hnwDialog.setFortschritt((int)(i*1.25)+2);
            int lw = loesung[i] & ~LWF;
            if (loesung[i] <= LWRT && loesung[i] > 9) {
                for (int k = 1; k <= 9; k++) {
                    if (lw != k) {
                        afg[i] = k;
                        strLoeser = new LevelLoeser(null, afg, lsg, false);                       
                        int lev = strLoeser.loeseAufgabe();                       
                        kopieren(aufgabe, afg, false);
                        if (lev > 0) {
                            eindeutig++;
//                            hnwDialog.setVisible(false);
//                            return false;
                        }
                    }
                }
            }
        }
//        eindeutig = true;
        hnwDialog.setVisible(false);
        return true;
    }
Zur Erklärung:
  1. Die Variable eindeutig habe ich von Boolean zu Integer umdefiniert und zähle damit die Anzahl der möglichen Lösungen (ich werden sie noch umbenennen).
  2. Wird mehr als eine Lösung gefunden, wird die Suche nicht abgebrochen, es werden alle Lösungen gezählt (ursprünglicher Code ist hier noch auskommentiert).
Das war es dann eigentlich schon. In der aufrufenden Methode hole ich mit echeck.is_eindeutig(), die Zahl der möglichen Lösungen (wird auch noch umbenannt) und gebe dann das Ergebnis an die jeweils aufrufende Methode zurück.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
N JavaFX Tableview nach Löschen von Element falscher Index AWT, Swing, JavaFX & SWT 4
T JavaFX Nach Zoomen wird an falscher Stelle gezeichnet AWT, Swing, JavaFX & SWT 0
T JavaFX Falscher Wert getLayoutBounds? + Tipps zum Code-Aufbau? AWT, Swing, JavaFX & SWT 8
N Swing Falscher Ansatz zu richtigem Ansatz AWT, Swing, JavaFX & SWT 10
L Swing Falscher Wert nach eigener Sortierung (JTable) AWT, Swing, JavaFX & SWT 2
T Tooltip bei falscher Eingabe anzeigen AWT, Swing, JavaFX & SWT 2
G JTable - bei falscher Eingabe wird alter Wert genommen. AWT, Swing, JavaFX & SWT 2
V falscher Unicode font AWT, Swing, JavaFX & SWT 6
M JTextarea mit falscher schriftgröse AWT, Swing, JavaFX & SWT 3
K falscher konstruktor? AWT, Swing, JavaFX & SWT 2
T Jtable falscher Wert wird übergeben AWT, Swing, JavaFX & SWT 13
B Swing Windowbuilder Rückgabewert im Event AWT, Swing, JavaFX & SWT 3
J Action Listener Rückgabewert AWT, Swing, JavaFX & SWT 14
S Swing Eigenen JDialog mit Rückgabewert AWT, Swing, JavaFX & SWT 3
A Swingworker rückgabewert verwenden AWT, Swing, JavaFX & SWT 9
J Swing JDialog mit static-Funktion anzeigen -> Rückgabewert AWT, Swing, JavaFX & SWT 3
C Warten auf Rückgabewert im EventDispatchingThread AWT, Swing, JavaFX & SWT 8
L Rückgabewert aus Klasse mit JDialog AWT, Swing, JavaFX & SWT 6
GilbertGrape Wie komm ich an Rückgabewert des SwingWorkers? AWT, Swing, JavaFX & SWT 6
R Rückgabewert TreeSelectionListener für JInternalFrame AWT, Swing, JavaFX & SWT 4
A showConfirmDialog (Rückgabewert abfragen?) AWT, Swing, JavaFX & SWT 3

Ähnliche Java Themen


Oben