Zu viele Wenn-Anweisungen?

263

Der folgende Code funktioniert zwar so, wie ich es brauche, aber er ist hässlich, übertrieben oder eine Reihe anderer Dinge. Ich habe mir Formeln angesehen und versucht, einige Lösungen zu schreiben, aber am Ende habe ich eine ähnliche Anzahl von Aussagen.

Gibt es eine Art mathematische Formel, die mir in diesem Fall zugute kommen würde, oder sind es 16, wenn Aussagen akzeptabel sind?

Um den Code zu erklären, handelt es sich um eine Art Spiel auf der Basis von simultanen Runden. Zwei Spieler haben jeweils vier Aktionsschaltflächen und die Ergebnisse stammen aus einem Array (0-3), aber die Variablen 'eins' und 'zwei' können sein etwas zugewiesen, wenn dies hilft. Das Ergebnis ist, 0 = kein Sieg, 1 = p1 gewinnt, 2 = p2 gewinnt, 3 = beide gewinnen.

public int fightMath(int one, int two) {

    if(one == 0 && two == 0) { result = 0; }
    else if(one == 0 && two == 1) { result = 0; }
    else if(one == 0 && two == 2) { result = 1; }
    else if(one == 0 && two == 3) { result = 2; }
    else if(one == 1 && two == 0) { result = 0; }
    else if(one == 1 && two == 1) { result = 0; }
    else if(one == 1 && two == 2) { result = 2; }
    else if(one == 1 && two == 3) { result = 1; }
    else if(one == 2 && two == 0) { result = 2; }
    else if(one == 2 && two == 1) { result = 1; }
    else if(one == 2 && two == 2) { result = 3; }
    else if(one == 2 && two == 3) { result = 3; }
    else if(one == 3 && two == 0) { result = 1; }
    else if(one == 3 && two == 1) { result = 2; }
    else if(one == 3 && two == 2) { result = 3; }
    else if(one == 3 && two == 3) { result = 3; }

    return result;
}
TomFirth
quelle
1
@waqaslam: - Dies kann der Java-Switch-Anweisung
Rahul Tripathi
9
Sicherlich gibt es hier eine Logik, die eher verallgemeinert als brutal gezwungen werden kann? Sicher gibt es eine Funktion f(a, b), die im allgemeinen Fall die Antwort liefert? Sie haben die Logik der Berechnung nicht erklärt, daher sind alle Antworten nur Lippenstift auf einem Schwein. Ich würde damit beginnen, Ihre Programmlogik ernsthaft zu überdenken. Die Verwendung von intFlags für Aktionen ist sehr veraltet. enums können Logik enthalten und sind beschreibend. Auf diese Weise können Sie Ihren Code auf modernere Weise schreiben.
Boris die Spinne
Nachdem ich die Antworten von @Steve Benett gelesen habe, die in seiner oben verlinkten alternativen Frage enthalten sind, kann ich davon ausgehen, dass es keine direkte Formelantwort darauf gibt, da es sich im Wesentlichen um eine Datenbank handelt. Ich habe versucht, in der ursprünglichen Frage zu erklären, dass ich ein einfaches Spiel (Kämpfer) gemacht habe und Benutzer eine Auswahl von 4 Schaltflächen haben: blockHigh (0), blockLow (1), attackHigh (2) und attackLow (3). Diese Nummern werden bis zur Verwendung in einem Array gespeichert. Später werden sie von der Funktion 'FightMath ()' verwendet, die die Auswahl von playerOne gegen playerTwos aufruft, um das Ergebnis zu erhalten. Keine tatsächliche Kollisionserkennung.
TomFirth
9
Wenn Sie eine Antwort haben, posten Sie diese bitte als solche. Die ausführliche Diskussion in den Kommentaren ist schwer zu verfolgen, insbesondere wenn es um Code geht. Wenn Sie darüber sprechen möchten, ob diese Frage auf Code Review migriert werden sollte, gibt es eine Meta- Diskussion darüber.
George Stocker
1
Was meinst du mit "das gleiche wie eine Datenbank"? Wenn sich diese Werte in der Datenbank befinden, ziehen Sie sie von dort ab. Andernfalls, wenn es wirklich so komplex ist, würde ich es so lassen, wie Sie es haben, und nach jeder Zeile Kommentare zur Geschäftslogik hinzufügen, damit die Leute verstehen, was los ist. Es ist besser (für mich) lang und explizit - jemand in der Zukunft kann verstehen, was los ist. Wenn Sie es in eine Karte einfügen oder versuchen, 8 Codezeilen zu speichern, ist der Vorteil sehr gering und die Verkleinerung größer: Sie machen es für jemanden, der Ihren Code eines Tages lesen muss, immer verwirrender.
Skaz

Antworten:

600

Wenn Sie keine Formel erstellen können, können Sie eine Tabelle für eine so begrenzte Anzahl von Ergebnissen verwenden:

final int[][] result = new int[][] {
  { 0, 0, 1, 2 },
  { 0, 0, 2, 1 },
  { 2, 1, 3, 3 },
  { 1, 2, 3, 3 }
};
return result[one][two];
laalto
quelle
7
Dies ist interessant, da ich diese Lösung noch nie gesehen habe. Ich bin mir nicht ganz sicher, ob ich das Rückgabeergebnis verstehe, werde es aber gerne testen.
TomFirth
4
Sie brauchen die Zusicherung nicht, Java wirft IndexOutOfBoundsExceptionsowieso eine, wenn ein oder mehrere Indizes außerhalb der Grenzen liegen.
JAB
43
@JoeHarper Wenn Sie etwas leicht lesbares möchten, werden Sie zunächst keine magischen Zahlen verwenden, und Sie erhalten einen Kommentar zur Erläuterung der Zuordnung. So wie es ist, ziehe ich diese Version dem Original vor, aber für etwas, das langfristig gewartet werden kann, würde ich einen Ansatz verwenden, der aufgezählte Typen oder zumindest benannte Konstanten umfasst.
JAB
13
@ JoeHarper "Theoretisch" ist eine Sache, "praktisch" ist eine andere. Natürlich versuche ich beschreibende Namensgebung zu verwenden (mit Ausnahme der i/ j/ kKonvention für Schleifenvariablen), benannte Konstanten, meinen Code in eine lesbaren Art und Weise der Anordnung, etc., aber wenn die Variable und Funktionsnamen mehr starten Aufnahme als 20 Zeichen Jedes, was ich finde, führt tatsächlich zu weniger lesbarem Code. Mein üblicher Ansatz ist es, hier und da nach verständlichem, aber prägnantem Code mit Kommentaren zu suchen, um zu erklären, warum der Code so strukturiert ist, wie er ist (und wie). Das Warum in die Namen zu setzen, macht einfach alles unübersichtlich.
JAB
13
Ich mag diese Lösung für dieses spezielle Problem, weil die Ergebnisse tatsächlich von einer Ergebnismatrix diktiert werden.
Versuch
201

Da Ihr Datensatz so klein ist, können Sie alles in eine lange Ganzzahl komprimieren und in eine Formel umwandeln

public int fightMath(int one,int two)
{
   return (int)(0xF9F66090L >> (2*(one*4 + two)))%4;
}

Bitweise Variante:

Dies nutzt die Tatsache, dass alles ein Vielfaches von 2 ist

public int fightMath(int one,int two)
{
   return (0xF9F66090 >> ((one << 3) | (two << 1))) & 0x3;
}

Der Ursprung der magischen Konstante

Was kann ich sagen? Die Welt braucht Magie, manchmal erfordert die Möglichkeit, dass etwas erschaffen wird.

Die Essenz der Funktion, die das Problem von OP löst, ist eine Zuordnung von 2 Zahlen (eins, zwei), Domäne {0,1,2,3} zum Bereich {0,1,2,3}. Jede der Antworten hat sich mit der Implementierung dieser Karte befasst.

Außerdem können Sie in einer Reihe von Antworten eine Wiederholung des Problems als Karte von 1 2-stelligen Basis 4-Nummer N (eins, zwei) sehen, wobei eins Ziffer 1, zwei Ziffer 2 und N = 4 * eins ist + zwei; N = {0,1,2, ..., 15} - 16 verschiedene Werte, das ist wichtig. Die Ausgabe der Funktion ist eine 1-stellige Basis-4-Zahl {0,1,2,3} - 4 verschiedene Werte, ebenfalls wichtig.

Nun kann eine 1-stellige Basis-4-Nummer als 2-stellige Basis-2-Nummer ausgedrückt werden. {0,1,2,3} = {00,01,10,11}, sodass jeder Ausgang mit nur 2 Bits codiert werden kann. Von oben sind nur 16 verschiedene Ausgänge möglich, sodass nur 16 * 2 = 32 Bit erforderlich sind, um die gesamte Karte zu codieren. Dies kann alles in eine ganze Zahl passen.

Die Konstante M ist eine Codierung der Abbildung m, wobei m (0) in Bits M [0: 1] codiert ist, m (1) in Bits M [2: 3] codiert ist und m (n) in Bits codiert ist M [n * 2: n * 2 + 1].

Alles, was bleibt, ist das Indizieren und Zurückgeben des rechten Teils der Konstante. In diesem Fall können Sie M 2 * N-mal nach rechts verschieben und die 2 niedrigstwertigen Bits nehmen, dh (M >> 2 * N) & 0x3. Die Ausdrücke (eins << 3) und (zwei << 1) multiplizieren nur die Dinge, während festgestellt wird, dass 2 * x = x << 1 und 8 * x = x << 3.

waTeim
quelle
79
klug, aber niemand sonst, der den Code liest, hat die Chance, ihn zu verstehen.
Aran Mulholland
106
Ich denke, das ist eine extrem schlechte Praxis. Niemand außer dem Autor wird das verstehen. Sie möchten sich einen Code ansehen und ihn schnell verstehen. Dies ist jedoch nur Zeitverschwendung.
Balázs Németh
14
Ich bin mit @ BalázsMáriaNémeth dabei. Obwohl sehr beeindruckend, sollten Sie für gewalttätige Psychopathen kodieren!
OrhanC1
90
Alle Downvoter denken, dass dies ein abscheulicher Codegeruch ist. Alle Aufsteiger denken gleich, bewundern aber die Klugheit dahinter. +1 (Verwenden Sie diesen Code niemals.)
usr
4
Was für ein schönes Beispiel dafür , nur Code zu schreiben !
England
98

Ich mag keine der vorgestellten Lösungen außer JABs. Keiner der anderen macht es einfach, den Code zu lesen und zu verstehen, was berechnet wird .

So würde ich diesen Code schreiben - ich kenne nur C #, nicht Java, aber Sie bekommen das Bild:

const bool t = true;
const bool f = false;
static readonly bool[,] attackResult = {
    { f, f, t, f }, 
    { f, f, f, t },
    { f, t, t, t },
    { t, f, t, t }
};
[Flags] enum HitResult 
{ 
    Neither = 0,
    PlayerOne = 1,
    PlayerTwo = 2,
    Both = PlayerOne | PlayerTwo
}
static HitResult ResolveAttack(int one, int two)
{
    return 
        (attackResult[one, two] ? HitResult.PlayerOne : HitResult.Neither) | 
        (attackResult[two, one] ? HitResult.PlayerTwo : HitResult.Neither);
}    

Jetzt ist viel klarer, was hier berechnet wird: Dies unterstreicht, dass wir berechnen, wer von welchem ​​Angriff getroffen wird, und beide Ergebnisse zurückgeben.

Dies könnte jedoch noch besser sein; das Boolesche Array ist etwas undurchsichtig. Ich mag den Table-Lookup-Ansatz, aber ich würde ihn gerne so schreiben, dass klar wird, wie die Semantik des Spiels aussehen soll. Das heißt, anstatt "ein Angriff von Null und eine Verteidigung von Eins führt zu keinem Treffer", finden Sie stattdessen einen Weg, den Code deutlicher zu machen, was impliziert, dass "ein Angriff mit geringem Tritt und eine Verteidigung mit niedrigem Block zu keinem Treffer führen". Stellen Sie sicher, dass der Code die Geschäftslogik des Spiels widerspiegelt.

Eric Lippert
quelle
66
Unsinn. Die meisten leicht erfahrenen Programme werden in der Lage sein, die hier gegebenen Ratschläge zu schätzen und den Codierungsstil auf ihre eigene Sprache anzuwenden. Die Frage war, wie man eine Reihe von Wenns vermeidet. Dies zeigt wie.
GreenAsJade
6
@ user3414693: Mir ist klar, dass es sich um eine Java-Frage handelt. Wenn Sie die Antwort sorgfältig lesen, wird dies klar. Wenn Sie glauben, dass meine Antwort unklug ist, ermutige ich Sie, Ihre eigene Antwort zu schreiben, die Ihnen besser gefällt.
Eric Lippert
1
@EricLippert Ich mag auch die Lösung von JAB. Meiner Meinung nach lässt der Aufzählungstyp in C # zu wünschen übrig. Es folgt nicht der Erfolgsphilosophie der übrigen Funktionen. Beispiel: stackoverflow.com/a/847353/92414 Hat das c # -Team Pläne, einen neuen Aufzählungstyp zu erstellen (um zu vermeiden, dass vorhandener Code beschädigt wird ), der besser gestaltet ist?
SolutionYogi
@SolutionYogi: Ich mag auch keine Aufzählungen in C #, obwohl sie aus guten historischen Gründen so sind, wie sie sind. (Hauptsächlich aus Gründen der Kompatibilität mit vorhandenen COM-Aufzählungen.) Mir sind keine Pläne bekannt, neue Ausrüstung für Aufzählungen in C # 6 hinzuzufügen.
Eric Lippert
3
@SList nein, Kommentare werden nicht ausgeführt. OP hat genau das getan, was getan werden sollte; Kommentare in Code konvertieren. Siehe zB Steve McConnell Code Complete stevemcconnell.com/cccntnt.htm
djechlin
87

Sie können eine Matrix erstellen, die Ergebnisse enthält

int[][] results = {{0, 0, 1, 2}, {0, 0, 2, 1},{2, 1, 3, 3},{2, 1, 3, 3}};

Wenn Sie Wert erhalten möchten, werden Sie verwenden

public int fightMath(int one, int two) {
  return this.results[one][two]; 
}
djm.im
quelle
69

Andere Leute haben bereits meine ursprüngliche Idee, die Matrixmethode, vorgeschlagen, aber zusätzlich zur Konsolidierung der if-Anweisungen können Sie einige Ihrer Probleme vermeiden, indem Sie sicherstellen, dass die angegebenen Argumente im erwarteten Bereich liegen, und indem Sie direkte Rückgaben verwenden (einige Codierungen) Standards, bei denen ich gesehen habe, dass One-Point-of-Exit für Funktionen erzwungen wird, aber ich habe festgestellt, dass Mehrfachrückgaben sehr nützlich sind, um die Pfeilcodierung zu vermeiden, und angesichts der Verbreitung von Ausnahmen in Java macht es ohnehin nicht viel Sinn, eine solche Regel strikt durchzusetzen da jede nicht erfasste Ausnahme, die innerhalb der Methode ausgelöst wird, ohnehin ein möglicher Austrittspunkt ist). Das Verschachteln von switch-Anweisungen ist eine Möglichkeit, aber für den kleinen Wertebereich, den Sie hier überprüfen, finde ich, dass Anweisungen kompakter sind und wahrscheinlich nicht zu einem großen Leistungsunterschied führen.

public int fightMath(int one, int two) {
    if (one > 3 || one < 0 || two > 3 || two < 0) {
        throw new IllegalArgumentException("Result is undefined for arguments outside the range [0, 3]");
    }

    if (one <= 1) {
        if (two <= 1) return 0;
        if (two - one == 2) return 1;
        return 2; // two can only be 3 here, no need for an explicit conditional
    }

    // one >= 2
    if (two >= 2) return 3;
    if (two == 1) return 1;
    return 2; // two can only be 0 here
}

Dies ist letztendlich weniger lesbar als es sonst aufgrund der Unregelmäßigkeit von Teilen der Eingabe-> Ergebniszuordnung sein könnte. Ich bevorzuge den Matrixstil stattdessen aufgrund seiner Einfachheit und der Art und Weise, wie Sie die Matrix so einrichten können, dass sie visuell sinnvoll ist (obwohl dies teilweise durch meine Erinnerungen an Karnaugh-Karten beeinflusst wird):

int[][] results = {{0, 0, 1, 2},
                   {0, 0, 2, 1},
                   {2, 1, 3, 3},
                   {2, 1, 3, 3}};

Update: In Anbetracht Ihrer Erwähnung von Blockieren / Schlagen ist hier eine radikalere Änderung der Funktion, die aufgezählte Typen mit Eigenschaften / Attributen für Eingaben und das Ergebnis verwendet und das Ergebnis ein wenig ändert, um das Blockieren zu berücksichtigen, was zu mehr führen sollte lesbare Funktion.

enum MoveType {
    ATTACK,
    BLOCK;
}

enum MoveHeight {
    HIGH,
    LOW;
}

enum Move {
    // Enum members can have properties/attributes/data members of their own
    ATTACK_HIGH(MoveType.ATTACK, MoveHeight.HIGH),
    ATTACK_LOW(MoveType.ATTACK, MoveHeight.LOW),
    BLOCK_HIGH(MoveType.BLOCK, MoveHeight.HIGH),
    BLOCK_LOW(MoveType.BLOCK, MoveHeight.LOW);

    public final MoveType type;
    public final MoveHeight height;

    private Move(MoveType type, MoveHeight height) {
        this.type = type;
        this.height = height;
    }

    /** Makes the attack checks later on simpler. */
    public boolean isAttack() {
        return this.type == MoveType.ATTACK;
    }
}

enum LandedHit {
    NEITHER,
    PLAYER_ONE,
    PLAYER_TWO,
    BOTH;
}

LandedHit fightMath(Move one, Move two) {
    // One is an attack, the other is a block
    if (one.type != two.type) {
        // attack at some height gets blocked by block at same height
        if (one.height == two.height) return LandedHit.NEITHER;

        // Either player 1 attacked or player 2 attacked; whoever did
        // lands a hit
        if (one.isAttack()) return LandedHit.PLAYER_ONE;
        return LandedHit.PLAYER_TWO;
    }

    // both attack
    if (one.isAttack()) return LandedHit.BOTH;

    // both block
    return LandedHit.NEITHER;
}

Sie müssen nicht einmal die Funktion selbst ändern, wenn Sie Blöcke / Angriffe mit mehr Höhen hinzufügen möchten, nur die Aufzählungen. Das Hinzufügen zusätzlicher Arten von Zügen erfordert jedoch wahrscheinlich eine Änderung der Funktion. Außerdem ist EnumSets möglicherweise erweiterbarer als die Verwendung zusätzlicher Aufzählungen als Eigenschaften der Hauptaufzählung, z. B. EnumSet<Move> attacks = EnumSet.of(Move.ATTACK_HIGH, Move.ATTACK_LOW, ...);und dann attacks.contains(move)eher als move.type == MoveType.ATTACK, obwohl die Verwendung von EnumSets wahrscheinlich etwas langsamer ist als direkte Gleichheitsprüfungen.


Für den Fall, dass ein erfolgreicher Block in einem Zähler ergibt, können Sie ersetzen if (one.height == two.height) return LandedHit.NEITHER;mit

if (one.height == two.height) {
    // Successful block results in a counter against the attacker
    if (one.isAttack()) return LandedHit.PLAYER_TWO;
    return LandedHit.PLAYER_ONE;
}

Das Ersetzen einiger ifAnweisungen durch die Verwendung des ternären Operators ( boolean_expression ? result_if_true : result_if_false) könnte den Code kompakter machen (zum Beispiel würde der Code im vorhergehenden Block werden return one.isAttack() ? LandedHit.PLAYER_TWO : LandedHit.PLAYER_ONE;), aber das kann zu schwer lesbaren Oneliner führen, so dass ich es nicht tun würde. Ich empfehle es für komplexere Verzweigungen.

JAB
quelle
Ich werde auf jeden Fall in diese aussehen , aber meine aktuellen Code ermöglicht es mir , den int - Wert zu verwenden oneund twoals Startpunkte auf meinem spritesheet wiederverwendet werden. Obwohl es nicht viel zusätzlichen Code erfordert, um dies zu ermöglichen.
TomFirth
2
@ TomFirth84 Es gibt eine EnumMapKlasse, mit der Sie die Aufzählungen Ihren ganzzahligen Offsets zuordnen können (Sie können auch die Ordnungswerte der Aufzählungselemente direkt verwenden, z. B. Move.ATTACK_HIGH.ordinal()wäre 0, Move.ATTACK_LOW.ordinal()wäre 1usw.), aber das ist fragiler / weniger flexibel als explizit Das Zuordnen jedes Mitglieds zu einem Wert als Hinzufügen von Aufzählungswerten zwischen vorhandenen Werten würde die Zählung beeinträchtigen, was bei einem nicht der Fall wäre EnumMap.)
JAB
7
Dies ist die am besten lesbare Lösung, da der Code in etwas übersetzt wird, das für die Person, die den Code liest, von Bedeutung ist.
David Stanley
Ihr Code, zumindest der mit Aufzählungen, ist falsch. Gemäß den if-Anweisungen in OP führt ein erfolgreicher Block zu einem Treffer für den Angreifer. Aber +1 für aussagekräftigen Code.
Taemyr
2
Sie können attack(against)der MoveAufzählung sogar eine Methode hinzufügen , die HIT zurückgibt, wenn der Zug ein erfolgreicher Angriff ist, BACKFIRE, wenn der Zug ein blockierter Angriff ist, und NICHTS, wenn es kein Angriff ist. Auf diese Weise können Sie es allgemein implementieren ( public boolean attack(Move other) { if this.isAttack() return (other.isAttack() || other.height != this.height) ? HIT : BACKFIRE; return NOTHING; }) und es bei Bedarf bei bestimmten Zügen überschreiben (schwache Züge, die jeder Block blockieren kann, Angriffe, die niemals nach hinten losgehen usw.)
neu geschrieben am
50

Warum nicht ein Array verwenden?

Ich werde von vorne beginnen. Ich sehe ein Muster, die Werte gehen von 0 bis 3 und Sie möchten alle möglichen Werte abfangen. Das ist dein Tisch:

0 & 0 = 0
0 & 1 = 0
0 & 2 = 1
0 & 3 = 2
1 & 0 = 0
1 & 1 = 0
1 & 2 = 2
1 & 3 = 1
2 & 0 = 2
2 & 1 = 1
2 & 2 = 3
2 & 3 = 3
3 & 0 = 2
3 & 1 = 1
3 & 2 = 3
3 & 3 = 3

Wenn wir uns dieselbe Tabellenbinärdatei ansehen, sehen wir die folgenden Ergebnisse:

00 & 00 = 00
00 & 01 = 00
00 & 10 = 01
00 & 11 = 10
01 & 00 = 00
01 & 01 = 00
01 & 10 = 10
01 & 11 = 01
10 & 00 = 10
10 & 01 = 01
10 & 10 = 11
10 & 11 = 11
11 & 00 = 10
11 & 01 = 01
11 & 10 = 11
11 & 11 = 11

Vielleicht sehen Sie bereits ein Muster, aber wenn ich den Wert eins und zwei kombiniere, sehe ich, dass Sie alle Werte 0000, 0001, 0010, ..... 1110 und 1111 verwenden. Nun kombinieren wir den Wert eins und zwei, um ein einziges zu erstellen 4-Bit-Ganzzahl.

0000 = 00
0001 = 00
0010 = 01
0011 = 10
0100 = 00
0101 = 00
0110 = 10
0111 = 01
1000 = 10
1001 = 01
1010 = 11
1011 = 11
1100 = 10
1101 = 01
1110 = 11
1111 = 11

Wenn wir dies zurück in Dezimalwerte übersetzen, sehen wir ein sehr mögliches Array von Werten, bei denen eins und zwei zusammen als Index verwendet werden können:

0 = 0
1 = 0
2 = 1
3 = 2
4 = 0
5 = 0
6 = 2
7 = 1
8 = 2
9 = 1
10 = 3
11 = 3
12 = 2
13 = 1
14 = 3
15 = 3

Das Array ist dann {0, 0, 1, 2, 0, 0, 2, 1, 2, 1, 3, 3, 2, 1, 3, 3}, wo sein Index einfach eins und zwei kombiniert ist.

Ich bin kein Java-Programmierer, aber Sie können alle if-Anweisungen loswerden und sie einfach so aufschreiben:

int[] myIntArray = {0, 0, 1, 2, 0, 0, 2, 1, 2, 1, 3, 3, 2, 1, 3, 3};
result = myIntArray[one * 4 + two]; 

Ich weiß nicht, ob eine Bitverschiebung um 2 schneller ist als eine Multiplikation. Aber es könnte einen Versuch wert sein.

dj bazzie wazzie
quelle
2
Eine Bitverschiebung von 2 ist fast definitiv schneller als eine Multiplikation von 4. Bestenfalls würde die Multiplikation mit 4 erkennen, dass 4 2 ^ 2 ist, und selbst eine Bitverschiebung durchführen (möglicherweise vom Compiler übersetzt). Ehrlich gesagt ist die Verschiebung für mich besser lesbar.
Cruncher
Ich mag deinen Ansatz! Es glättet im Wesentlichen eine 4x4-Matrix zu einem 16-Elemente-Array.
Cameron Tinker
6
Wenn ich mich nicht irre, wird der Compiler heutzutage zweifellos erkennen, dass Sie mit einer Zweierpotenz multiplizieren, und es entsprechend optimieren. Für Sie, den Programmierer, sollten die Bitverschiebung und die Multiplikation genau die gleiche Leistung haben.
Tanner Swett
24

Dies verwendet ein wenig Bitmagie (Sie tun dies bereits, indem Sie zwei Informationsbits (niedrig / hoch & Angriff / Block) in einer einzigen Ganzzahl speichern):

Ich habe es nicht ausgeführt, sondern nur hier eingegeben. Bitte überprüfen Sie es noch einmal. Die Idee funktioniert sicherlich. EDIT: Es wird jetzt für jede Eingabe getestet, funktioniert gut.

public int fightMath(int one, int two) {
    if(one<2 && two<2){ //both players blocking
        return 0; // nobody hits
    }else if(one>1 && two>1){ //both players attacking
        return 3; // both hit
    }else{ // some of them attack, other one blocks
        int different_height = (one ^ two) & 1; // is 0 if they are both going for the same height - i.e. blocker wins, and 1 if height is different, thus attacker wins
        int attacker = one>1?1:0; // is 1 if one is the attacker, two is the blocker, and 0 if one is the blocker, two is the attacker
        return (attacker ^ different_height) + 1;
    }
}

Oder sollte ich vorschlagen, die beiden Informationsbits in separate Variablen zu trennen? Code, der hauptsächlich auf Bitoperationen wie der oben genannten basiert, ist normalerweise sehr schwer zu pflegen.

elias
quelle
2
Ich stimme dieser Lösung zu und sehe sehr nach dem aus, was ich in meinem Kommentar zur Hauptfrage im Sinn hatte. Ich würde es vorziehen, es in separate Variablen aufzuteilen, um es beispielsweise in Zukunft einfacher zu machen, einen mittleren Angriff hinzuzufügen.
Yoh
2
Ich habe gerade einige Fehler im obigen Code behoben, nachdem ich ihn getestet habe. Jetzt funktioniert er gut. Als ich weiter auf dem Weg des Bitmanipulators ging, fand ich auch eine einzeilige Lösung, die immer noch nicht so mystisch ist wie die Bitmaske in anderen Antworten, aber immer noch schwierig genug ist, um Ihre Gedanken zu return ((one ^ two) & 2) == 0 ? (one & 2) / 2 * 3 : ((one & 2) / 2 ^ ((one ^ two) & 1)) + 1;
verwirren
1
Dies ist die beste Antwort, da jeder neue Programmierer, der sie liest, tatsächlich die Magie versteht, die hinter all diesen magischen Zahlen steckt.
Bezmax
20

Um ganz ehrlich zu sein, hat jeder seinen eigenen Codestil. Ich hätte nicht gedacht, dass die Leistung zu stark beeinträchtigt würde. Wenn Sie dies besser verstehen als die Verwendung einer Switch-Case-Version, verwenden Sie diese weiterhin.

Sie könnten die ifs verschachteln, sodass sich möglicherweise eine leichte Leistungssteigerung für Ihre letzten if-Prüfungen ergeben würde, da nicht so viele if-Anweisungen durchlaufen worden wären. Aber in Ihrem Kontext eines Java-Grundkurses wird es wahrscheinlich keinen Nutzen bringen.

else if(one == 3 && two == 3) { result = 3; }

Also, anstatt ...

if(one == 0 && two == 0) { result = 0; }
else if(one == 0 && two == 1) { result = 0; }
else if(one == 0 && two == 2) { result = 1; }
else if(one == 0 && two == 3) { result = 2; }

Du würdest tun ...

if(one == 0) 
{ 
    if(two == 0) { result = 0; }
    else if(two == 1) { result = 0; }
    else if(two == 2) { result = 1; }
    else if(two == 3) { result = 2; }
}

Und formatieren Sie es einfach neu, wie Sie es bevorzugen.

Dies lässt den Code nicht besser aussehen, beschleunigt ihn aber möglicherweise ein wenig, glaube ich.

Joe Harper
quelle
3
Ich weiß nicht, ob es wirklich eine gute Praxis ist, aber für diesen Fall würde ich wahrscheinlich verschachtelte switch-Anweisungen verwenden. Es würde mehr Platz beanspruchen, aber es wäre wirklich klar.
Dyesdyes
Das würde auch funktionieren, aber ich denke, es ist eine Frage der Präferenz. Ich bevorzuge eigentlich if-Anweisungen, da sie virtuell sagen, was der Code tut. Natürlich nicht deine Meinung niederlegen, was auch immer für dich funktioniert :). Upvote für den alternativen Vorschlag!
Joe Harper
12

Mal sehen, was wir wissen

1: Ihre Antworten sind symmetrisch für P1 (Spieler eins) und P2 (Spieler zwei). Dies ist für ein Kampfspiel sinnvoll, kann aber auch genutzt werden, um Ihre Logik zu verbessern.

2: 3 Schläge 0 Schläge 2 Schläge 1 Schläge 3. Die einzigen Fälle, die von diesen Fällen nicht abgedeckt werden, sind Kombinationen von 0 gegen 1 und 2 gegen 3. Anders ausgedrückt sieht die einzigartige Siegertabelle folgendermaßen aus: 0 Schläge 2, 1 Schläge 3, 2 Schläge 1, 3 Schläge 0.

3: Wenn 0/1 gegeneinander antreten, gibt es ein unentschiedenes Unentschieden, aber wenn 2/3 gegeneinander antreten, treffen beide

Lassen Sie uns zunächst eine Einwegfunktion erstellen, die uns sagt, ob wir gewonnen haben:

// returns whether we beat our opponent
public boolean doesBeat(int attacker, int defender) {
  int[] beats = {2, 3, 1, 0};
  return defender == beats[attacker];
}

Mit dieser Funktion können wir dann das Endergebnis zusammenstellen:

// returns the overall fight result
// bit 0 = one hits
// bit 1 = two hits
public int fightMath(int one, int two)
{
  // Check to see whether either has an outright winning combo
  if (doesBeat(one, two))
    return 1;

  if (doesBeat(two, one))
    return 2;

  // If both have 0/1 then its hitless draw but if both have 2/3 then they both hit.
  // We can check this by seeing whether the second bit is set and we need only check
  // one's value as combinations where they don't both have 0/1 or 2/3 have already
  // been dealt with 
  return (one & 2) ? 3 : 0;
}

Obwohl dies wohl komplexer und wahrscheinlich langsamer ist als die in vielen Antworten angebotene Tabellensuche, halte ich es für eine überlegene Methode, da sie tatsächlich die Logik Ihres Codes kapselt und sie jedem beschreibt, der Ihren Code liest. Ich denke, das macht es zu einer besseren Implementierung.

(Es ist schon eine Weile her, dass ich Java gemacht habe, also entschuldige mich, wenn die Syntax nicht stimmt. Hoffentlich ist es immer noch verständlich, wenn ich etwas falsch verstanden habe.)

0-3 bedeutet übrigens eindeutig etwas; Sie sind keine willkürlichen Werte, daher wäre es hilfreich, sie zu benennen.

Jack Aidley
quelle
11

Ich hoffe ich verstehe die Logik richtig. Wie wäre es mit so etwas wie:

public int fightMath (int one, int two)
{
    int oneHit = ((one == 3 && two != 1) || (one == 2 && two != 0)) ? 1 : 0;
    int twoHit = ((two == 3 && one != 1) || (two == 2 && one != 0)) ? 2 : 0;

    return oneHit+twoHit;
}

Das Aktivieren eines Treffers hoch oder eines Treffers niedrig ist nicht blockiert und das Gleiche gilt für Spieler zwei.

Bearbeiten: Algorithmus wurde nicht vollständig verstanden, "Treffer" beim Blockieren vergeben, was ich nicht realisiert habe (Thx elias):

public int fightMath (int one, int two)
{
    int oneAttack = ((one == 3 && two != 1) || (one == 2 && two != 0)) ? 1 : (one >= 2) ? 2 : 0;
    int twoAttack = ((two == 3 && one != 1) || (two == 2 && one != 0)) ? 2 : (two >= 2) ? 1 : 0;

    return oneAttack | twoAttack;
}
Chris
quelle
Weg, um das Muster tolles Ergebnis zu finden!
Chad
1
Ich mag den Ansatz, aber ich befürchte, dass dieser Lösung die Möglichkeit eines Treffers durch Blockieren eines Angriffs völlig fehlt (z. B. wenn eins = 0 und zwei = 2, gibt sie 0 zurück, aber 1 wird gemäß der Spezifikation erwartet). Vielleicht können Sie daran arbeiten, um es richtig zu machen, aber ich bin mir nicht sicher, ob der resultierende Code immer noch so elegant ist, da dies bedeutet, dass die Zeilen etwas länger werden.
Elias
Wusste nicht, dass ein "Treffer" für Block vergeben wurde. Vielen Dank für den Hinweis. Mit sehr einfacher Lösung eingestellt.
Chris
10

Ich habe keine Erfahrung mit Java, daher kann es zu Tippfehlern kommen. Bitte betrachten Sie den Code als Pseudocode.

Ich würde mit einem einfachen Schalter gehen. Dafür benötigen Sie eine einzelne Zahlenbewertung. In diesem Fall können wir jedoch seit 0 <= one < 4 <= 9und 0 <= two < 4 <= 9beide Ints in ein einfaches int konvertieren, indem wir onemit 10 multiplizieren und addieren two. Verwenden Sie dann einen Schalter in der resultierenden Zahl wie folgt:

public int fightMath(int one, int two) {
    // Convert one and two to a single variable in base 10
    int evaluate = one * 10 + two;

    switch(evaluate) {
        // I'd consider a comment in each line here and in the original code
        // for clarity
        case 0: result = 0; break;
        case 1: result = 0; break;
        case 1: result = 0; break;
        case 2: result = 1; break;
        case 3: result = 2; break;
        case 10: result = 0; break;
        case 11: result = 0; break;
        case 12: result = 2; break;
        case 13: result = 1; break;
        case 20: result = 2; break;
        case 21: result = 1; break;
        case 22: result = 3; break;
        case 23: result = 3; break;
        case 30: result = 1; break;
        case 31: result = 2; break;
        case 32: result = 3; break;
        case 33: result = 3; break;
    }

    return result;
}

Es gibt noch eine andere kurze Methode, auf die ich nur als theoretischen Code hinweisen möchte. Ich würde es jedoch nicht verwenden, da es eine zusätzliche Komplexität aufweist, mit der Sie normalerweise nicht umgehen möchten. Die zusätzliche Komplexität ergibt sich aus der Basis 4 , da die Zählung 0, 1, 2, 3, 10, 11, 12, 13, 20, ... ist.

public int fightMath(int one, int two) {
    // Convert one and two to a single variable in base 4
    int evaluate = one * 4 + two;

    allresults = new int[] { 0, 0, 1, 2, 0, 0, 2, 1, 2, 1, 3, 3, 1, 2, 3, 3 };

    return allresults[evaluate];
}

Wirklich nur ein zusätzlicher Hinweis, falls mir etwas in Java fehlt. In PHP würde ich tun:

function fightMath($one, $two) {
    // Convert one and two to a single variable in base 4
    $evaluate = $one * 10 + $two;

    $allresults = array(
         0 => 0,  1 => 0,  2 => 1,  3 => 2,
        10 => 0, 11 => 0, 12 => 2, 13 => 1,
        20 => 2, 21 => 1, 22 => 3, 23 => 3,
        30 => 1, 31 => 2, 32 => 3, 33 => 3 );

    return $allresults[$evaluate];
}
Francisco Presencia
quelle
Java hat keine Lamdas vor der 8. Version
Kirill Gamazkov
1
Dies. Für eine so kleine Anzahl von Eingängen würde ich einen Schalter mit einem zusammengesetzten Wert verwenden (obwohl er mit einem Multiplikator größer als 10, z. B. 100 oder 1000, besser lesbar sein kann).
Medinoc
7

Da Sie verschachtelte ifBedingungen bevorzugen , gibt es hier einen anderen Weg.
Beachten Sie, dass das resultMitglied nicht verwendet wird und kein Status geändert wird.

public int fightMath(int one, int two) {
    if (one == 0) {
      if (two == 0) { return 0; }
      if (two == 1) { return 0; }
      if (two == 2) { return 1; }
      if (two == 3) { return 2; }
    }   
    if (one == 1) {
      if (two == 0) { return 0; }
      if (two == 1) { return 0; }
      if (two == 2) { return 2; }
      if (two == 3) { return 1; }
    }
    if (one == 2) {
      if (two == 0) { return 2; }
      if (two == 1) { return 1; }
      if (two == 2) { return 3; }
      if (two == 3) { return 3; }
    }
    if (one == 3) {
      if (two == 0) { return 1; }
      if (two == 1) { return 2; }
      if (two == 2) { return 3; }
      if (two == 3) { return 3; }
    }
    return DEFAULT_RESULT;
}
Nick Dandoulakis
quelle
Warum hast du keine anderen?
FDinoff
3
@FDinoff Ich hätte elseKetten benutzen können, aber es hatte keinen Unterschied gemacht.
Nick Dandoulakis
1
Ich weiß, dass es trivial ist, aber würde das Hinzufügen der else-Ketten nicht schneller ausgeführt werden? in 3 von 4 Fällen? Ich habe immer die Gewohnheit, Code zu schreiben, um ihn so schnell wie möglich auszuführen, auch wenn es nur wenige Zyklen sind.
Brandon Bearden
2
@BrandonBearden hier machen sie keinen Unterschied (vorausgesetzt, die Eingabe liegt immer im Bereich 0..3). Der Compiler wird den Code wahrscheinlich trotzdem mikrooptimieren. Wenn wir eine lange Reihe von else ifAnweisungen haben, können wir den Code mit switchoder Nachschlagetabellen beschleunigen .
Nick Dandoulakis
Wie ist das so Wenn one==0der Code ausgeführt wird, muss geprüft werden, ob one==1dann ob one==2und schließlich ob one==3- Wenn noch weitere if vorhanden wären, würden die letzten drei Überprüfungen nicht durchgeführt, da die Anweisung nach der ersten Übereinstimmung beendet würde. Und ja, Sie könnten weiter optimieren, indem Sie anstelle der if (one...Anweisungen eine switch-Anweisung und dann im Fall von eine weitere switch-Anweisung verwenden one's. Das ist jedoch nicht meine Frage.
Brandon Bearden
6

Versuchen Sie es mit Schaltergehäuse. ..

Schauen Sie hier oder hier nach, um weitere Informationen zu erhalten

switch (expression)
{ 
  case constant:
        statements;
        break;
  [ case constant-2:
        statements;
        break;  ] ...
  [ default:
        statements;
        break;  ] ...
}

Sie können mehrere Bedingungen (nicht gleichzeitig) hinzufügen und sogar eine Standardoption haben, bei der keine anderen Fälle erfüllt wurden.

PS: Nur wenn eine Bedingung erfüllt sein soll ..

Wenn 2 Bedingungen gleichzeitig auftreten, kann der Schalter nicht verwendet werden. Aber Sie können Ihren Code hier reduzieren.

Java-Switch-Anweisung mehrere Fälle

Nevin Madhukar K.
quelle
6

Das erste, was mir einfiel, war im Wesentlichen die gleiche Antwort von Francisco Presencia, aber etwas optimiert:

public int fightMath(int one, int two)
{
    switch (one*10 + two)
    {
    case  0:
    case  1:
    case 10:
    case 11:
        return 0;
    case  2:
    case 13:
    case 21:
    case 30:
        return 1;
    case  3:
    case 12:
    case 20:
    case 31:
        return 2;
    case 22:
    case 23:
    case 32:
    case 33:
        return 3;
    }
}

Sie können es weiter optimieren, indem Sie den letzten Fall (für 3) zum Standardfall machen:

    //case 22:
    //case 23:
    //case 32:
    //case 33:
    default:
        return 3;

Der Vorteil dieser Methode besteht darin, dass leichter zu erkennen ist, welche Werte für welche Rückgabewerte gelten oneund twowelchen entsprechen, als bei einigen anderen vorgeschlagenen Methoden.

David R Tribble
quelle
Dies ist eine Variation einer anderen Antwort von mir hier .
David R Tribble
6
((two&2)*(1+((one^two)&1))+(one&2)*(2-((one^two)&1)))/2
Dawood ibn Kareem
quelle
4
Wie lange haben Sie gebraucht, um dazu zu gelangen?
mbatchkarov
2
@mbatchkarov Etwa 10 Minuten Lesen der anderen Antworten, dann 10 Minuten Kritzeln mit Bleistift und Papier.
Dawood ibn Kareem
7
Ich wäre wirklich traurig, wenn ich das beibehalten müsste.
Meryovi
ähmmm ... Es gibt einen Fehler: Sie werden vermisst; --unicorns
Alberto
Ich stimme @Meryovi zu, Requisiten für Prägnanz und doch schrecklich wie APL-Code
Ed Griebel
4

Sie können anstelle eines Mehrfachschalters einen Schalter verwendenif

Um zu erwähnen, dass Sie, da Sie zwei Variablen haben, die beiden Variablen zusammenführen müssen, um sie im Schalter zu verwenden

Überprüfen Sie diese Java-switch-Anweisung, um zwei Variablen zu verarbeiten.

Rahul Tripathi
quelle
3

Wenn ich eine Tabelle zwischen eins / zwei und dem Ergebnis zeichne, sehe ich ein Muster:

if(one<2 && two <2) result=0; return;

Das Obige würde mindestens 3 if-Aussagen reduzieren. Ich sehe weder ein festgelegtes Muster noch kann ich viel aus dem angegebenen Code lernen - aber wenn eine solche Logik abgeleitet werden kann, würde dies eine Reihe von if-Anweisungen reduzieren.

Hoffe das hilft.

AnonNihcas
quelle
3

Ein guter Punkt wäre, die Regeln als Text zu definieren. Sie können dann leichter die richtige Formel ableiten. Dies wird aus Laaltos netter Array-Darstellung extrahiert:

{ 0, 0, 1, 2 },
{ 0, 0, 2, 1 },
{ 2, 1, 3, 3 },
{ 1, 2, 3, 3 }

Und hier gehen wir mit einigen allgemeinen Kommentaren, aber Sie sollten sie in Regelbegriffen beschreiben:

if(one<2) // left half
{
    if(two<2) // upper left half
    {
        result = 0; //neither hits
    }
    else // lower left half
    {
        result = 1+(one+two)%2; //p2 hits if sum is even
    }
}
else // right half
{
    if(two<2) // upper right half
    {
        result = 1+(one+two+1)%2; //p1 hits if sum is even
    }
    else // lower right half
    {
        return 3; //both hit
    }
}

Sie könnten dies natürlich auf weniger Code reduzieren, aber es ist im Allgemeinen eine gute Idee, zu verstehen, was Sie codieren, anstatt eine kompakte Lösung zu finden.

if((one<2)&&(two<2)) result = 0; //top left
else if((one>1)&&(two>1)) result = 3; //bottom right
else result = 1+(one+two+((one>1)?1:0))%2; //no idea what that means

Eine Erklärung zu den komplizierten p1 / p2-Treffern wäre toll, sieht interessant aus!

Marcellus
quelle
3

Die kürzeste und noch lesbare Lösung:

static public int fightMath(int one, int two)
{
    if (one < 2 && two < 2) return 0;
    if (one > 1 && two > 1) return 3;
    int n = (one + two) % 2;
    return one < two ? 1 + n : 2 - n;
}

oder noch kürzer:

static public int fightMath(int one, int two)
{
    if (one / 2 == two / 2) return (one / 2) * 3;
    return 1 + (one + two + one / 2) % 2;
}

Enthält keine "magischen" Zahlen;) Hoffe es hilft.

PW
quelle
2
Formeln wie diese machen es unmöglich, das Ergebnis einer Kombination zu einem späteren Zeitpunkt zu ändern (zu aktualisieren). Der einzige Weg wäre, die gesamte Formel zu überarbeiten.
SNag
2
@ Snag: Dem stimme ich zu. Die flexibelste Lösung ist die Verwendung des 2D-Arrays. Aber der Autor dieses Beitrags wollte eine Formel und dies ist die beste, die Sie mit nur einfachem Wenn und Mathematik bekommen können.
PW
2

static int val(int i, int u){ int q = (i & 1) ^ (u & 1); return ((i >> 1) << (1 ^ q))|((u >> 1) << q); }

user1837841
quelle
1

Ich persönlich mag es, ternäre Operatoren zu kaskadieren:

int result = condition1
    ? result1
    : condition2
    ? result2
    : condition3
    ? result3
    : resultElse;

In Ihrem Fall können Sie jedoch Folgendes verwenden:

final int[] result = new int[/*16*/] {
    0, 0, 1, 2,
    0, 0, 2, 1,
    2, 1, 3, 3,
    1, 2, 3, 3
};

public int fightMath(int one, int two) {
    return result[one*4 + two];
}

Oder Sie können ein Muster in Bits bemerken:

one   two   result

section 1: higher bits are equals =>
both result bits are equals to that higher bits

00    00    00
00    01    00
01    00    00
01    01    00
10    10    11
10    11    11
11    10    11
11    11    11

section 2: higher bits are different =>
lower result bit is inverse of lower bit of 'two'
higher result bit is lower bit of 'two'

00    10    01
00    11    10
01    10    10
01    11    01
10    00    10
10    01    01
11    00    01
11    01    10

So können Sie Magie verwenden:

int fightMath(int one, int two) {
    int b1 = one & 2, b2 = two & 2;
    if (b1 == b2)
        return b1 | (b1 >> 1);

    b1 = two & 1;

    return (b1 << 1) | (~b1);
}
Kirill Gamazkov
quelle
1

Hier ist eine ziemlich prägnante Version, ähnlich der Antwort von JAB . Dabei wird eine Karte zum Speichern verwendet, die den Triumph über andere bewegt.

public enum Result {
  P1Win, P2Win, BothWin, NeitherWin;
}

public enum Move {
  BLOCK_HIGH, BLOCK_LOW, ATTACK_HIGH, ATTACK_LOW;

  static final Map<Move, List<Move>> beats = new EnumMap<Move, List<Move>>(
      Move.class);

  static {
    beats.put(BLOCK_HIGH, new ArrayList<Move>());
    beats.put(BLOCK_LOW, new ArrayList<Move>());
    beats.put(ATTACK_HIGH, Arrays.asList(ATTACK_LOW, BLOCK_LOW));
    beats.put(ATTACK_LOW, Arrays.asList(ATTACK_HIGH, BLOCK_HIGH));
  }

  public static Result compare(Move p1Move, Move p2Move) {
    boolean p1Wins = beats.get(p1Move).contains(p2Move);
    boolean p2Wins = beats.get(p2Move).contains(p1Move);

    if (p1Wins) {
      return (p2Wins) ? Result.BothWin : Result.P1Win;
    }
    if (p2Wins) {
      return (p1Wins) ? Result.BothWin : Result.P2Win;
    }

    return Result.NeitherWin;
  }
} 

Beispiel:

System.out.println(Move.compare(Move.ATTACK_HIGH, Move.BLOCK_LOW));

Drucke:

P1Win
Duncan Jones
quelle
Ich würde static final Map<Move, List<Move>> beats = new java.util.EnumMap<>();stattdessen empfehlen , es sollte etwas effizienter sein.
JAB
@ JAB Ja, gute Idee. Ich vergesse immer, dass dieser Typ existiert. Und ... wie umständlich ist es, einen zu konstruieren!
Duncan Jones
1

Ich würde eine Map verwenden, entweder eine HashMap oder eine TreeMap

Vor allem, wenn die Parameter nicht im Formular enthalten sind 0 <= X < N

Wie eine Reihe von zufälligen positiven ganzen Zahlen.

Code

public class MyMap
{
    private TreeMap<String,Integer> map;

    public MyMap ()
    {
        map = new TreeMap<String,Integer> ();
    }

    public void put (int key1, int key2, Integer value)
    {
        String key = (key1+":"+key2);

        map.put(key, new Integer(value));
    }

    public Integer get (int key1, int key2)
    {
        String key = (key1+":"+key2);

        return map.get(key);
    }
}
Khaled.K
quelle
1

Vielen Dank an @Joe Harper, als ich eine Variation seiner Antwort verwendete. Um es weiter abzunehmen, da 2 Ergebnisse pro 4 gleich waren, habe ich es weiter abgenommen.

Ich werde vielleicht irgendwann darauf zurückkommen, aber wenn es keinen größeren Widerstand gibt, der durch mehrere ifAussagen verursacht wird, werde ich dies vorerst beibehalten. Ich werde mich weiter mit der Tabellenmatrix und den Switch-Anweisungslösungen befassen.

public int fightMath(int one, int two) {
  if (one === 0) {
    if (two === 2) { return 1; }
    else if(two === 3) { return 2; }
    else { return 0; }
  } else if (one === 1) {
    if (two === 2) { return 2; }
    else if (two === 3) { return 1; }
    else { return 0; }
  } else if (one === 2) {
    if (two === 0) { return 2; }
    else if (two === 1) { return 1; }
    else { return 3; }
  } else if (one === 3) {
    if (two === 0) { return 1; }
    else if (two === 1) { return 2; }
    else { return 3; }
  }
}
TomFirth
quelle
13
Dies ist tatsächlich weniger lesbar als das Original und reduziert nicht die Anzahl der if-Anweisungen ...
Chad
@Chad Die Idee dabei war, die Prozessgeschwindigkeit zu erhöhen, und obwohl es schrecklich aussieht, kann es leicht aktualisiert werden, sollte ich in Zukunft weitere Aktionen hinzufügen. Wenn ich das sage, verwende ich jetzt eine vorherige Antwort, die ich vorher nicht vollständig verstanden habe.
TomFirth
3
@ TomFirth84 Gibt es einen Grund, warum Sie die richtigen Codierungskonventionen für Ihre if-Anweisungen nicht befolgen?
ylun.ca
@ylun: Ich hatte die Zeilen vor dem Einfügen in SO reduziert, nicht aus Gründen der Lesbarkeit, sondern aus Gründen des Spam-Speicherplatzes. Es gibt Variationen der Praxis auf dieser Seite und leider ist es genau die Art und Weise, wie ich gelernt habe und mich damit wohl fühle.
TomFirth
2
@ TomFirth84 Ich denke nicht, dass dies leicht zu aktualisieren ist, die Anzahl der Zeilen wächst so etwas wie das Produkt der Anzahl der zulässigen Werte.
Andrew Lazarus
0
  1. Verwenden Sie Konstanten oder Aufzählungen, um den Code besser lesbar zu machen
  2. Versuchen Sie, den Code in mehrere Funktionen aufzuteilen
  3. Versuchen Sie, die Symmetrie des Problems zu verwenden

Hier ist ein Vorschlag, wie dies aussehen könnte, aber die Verwendung eines Ints hier ist immer noch etwas hässlich:

static final int BLOCK_HIGH = 0;
static final int BLOCK_LOW = 1;
static final int ATTACK_HIGH = 2;
static final int ATTACK_LOW = 3;

public static int fightMath(int one, int two) {
    boolean player1Wins = handleAttack(one, two);
    boolean player2Wins = handleAttack(two, one);
    return encodeResult(player1Wins, player2Wins); 
}



private static boolean handleAttack(int one, int two) {
     return one == ATTACK_HIGH && two != BLOCK_HIGH
        || one == ATTACK_LOW && two != BLOCK_LOW
        || one == BLOCK_HIGH && two == ATTACK_HIGH
        || one == BLOCK_LOW && two == ATTACK_LOW;

}

private static int encodeResult(boolean player1Wins, boolean player2Wins) {
    return (player1Wins ? 1 : 0) + (player2Wins ? 2 : 0);
}

Es wäre schöner, einen strukturierten Typ für die Eingabe und die Ausgabe zu verwenden. Die Eingabe hat tatsächlich zwei Felder: die Position und den Typ (Block oder Angriff). Die Ausgabe enthält außerdem zwei Felder: player1Wins und player2Wins. Das Codieren in eine einzelne Ganzzahl erschwert das Lesen des Codes.

class PlayerMove {
    PlayerMovePosition pos;
    PlayerMoveType type;
}

enum PlayerMovePosition {
    HIGH,LOW
}

enum PlayerMoveType {
    BLOCK,ATTACK
}

class AttackResult {
    boolean player1Wins;
    boolean player2Wins;

    public AttackResult(boolean player1Wins, boolean player2Wins) {
        this.player1Wins = player1Wins;
        this.player2Wins = player2Wins;
    }
}

AttackResult fightMath(PlayerMove a, PlayerMove b) {
    return new AttackResult(isWinningMove(a, b), isWinningMove(b, a));
}

boolean isWinningMove(PlayerMove a, PlayerMove b) {
    return a.type == PlayerMoveType.ATTACK && !successfulBlock(b, a)
            || successfulBlock(a, b);
}

boolean successfulBlock(PlayerMove a, PlayerMove b) {
    return a.type == PlayerMoveType.BLOCK 
            && b.type == PlayerMoveType.ATTACK 
            && a.pos == b.pos;
}

Leider ist Java nicht sehr gut darin, solche Datentypen auszudrücken.

peq
quelle
-2

Mach stattdessen so etwas

   public int fightMath(int one, int two) {
    return Calculate(one,two)

    }


    private int Calculate(int one,int two){

    if (one==0){
        if(two==0){
     //return value}
    }else if (one==1){
   // return value as per condtiion
    }

    }
Onkar
quelle
4
Sie haben gerade eine private Funktion erstellt, die von der öffentlichen Funktion umschlossen wird. Warum nicht einfach in der öffentlichen Funktion umsetzen?
Martin Ueding
5
Und Sie haben die Anzahl der if-Anweisungen nicht reduziert.
Chad