Disegni per saltare a un punto nel codice con il codice più pulito possibile (senza goto)

2

Voglio essere in grado di avere un (piuttosto) grande FSM in cui posso eseguire il debug saltando a un punto e osservando l'esecuzione.

Ad esempio, diciamo che sto facendo un FSM che usa una certa probabilità se vado a sinistra o a destra a un incrocio. Forse ho un sacco di cose come "le persone che ho visto andare a sinistra" e "le persone che ho visto vanno bene".

Supponiamo che il semplice codice sopra sia simile a questo:

if (somethingA()) {
    if (somethingB()) {
        if (somethingX() && somethingY()) {
            // ...
        } else {
            // ...
        }
    } else if (somethingC()) {
        // ...
    }
} else {
    // ...
}

Inoltre, supponi che tutte le funzioni di qualcosa ... () siano basate sulla probabilità!

Il codice sopra è brutto, ma solo per illustrazione.

C'è qualche tipo di design che mi permetterebbe di andare arbitrariamente in un albero? Dato che sarebbe distribuito su più funzioni (di nuovo questo non è un FSM di dimensioni ridotte), un modello di progettazione potrebbe aiutarmi qui?

Idealmente vorrei sondare come funziona saltando fino al punto prima di somethingX() && somethingY() se cambio alcuni parametri di probabilità per vedere come reagisce dopo, e vediamo come funziona andando da lì. Ad esempio, se ho 10 funzioni annidate come sopra, vorrei passare al layer 8, regolare la probabilità di una delle chiamate per vedere come funziona, e quindi dividere un gruppo di chiamate da quella.

Con questo intendo saltare al punto sopra, eseguire una simulazione, ma chiamare questa simulazione forse 1000 volte da quel punto (e vorrei eseguirlo il più rapidamente possibile e multithread).

L'unica soluzione che riesco a pensare è di aggiungere una sorta di POD (questo è C ++) e impostarlo in anticipo in modo da poter saltare esattamente dove voglio, come:

if (myStruct.jumpThroughA || somethingA()) {
    if (myStruct.jumpThroughB || somethingB()) {
        if (myStruct.jumpThroughXY || (somethingX() && somethingY())) {
            // ...
        } else {
            // ...
        }
    } else if (myStruct.jumpThroughC || somethingC()) {
        // ...
    }
} else {
    // ...
}

Tuttavia sono preoccupato che farò codice clusterfuck facendo questo (ma potrebbe essere la mia unica opzione). Voglio davvero evitare questo codice a meno che la mia schiena non sia contro il muro.

Inoltre, una cosa del genere sarebbe ottima per testare qualcosa di specifico, soprattutto perché la casualità avrà un ruolo in queste cose e mi piacerebbe testarlo forzandone alcuni rami (la mia idea di un PRNG prevedibile per i test potrebbe o potrebbe non renderlo ancora un rompicoglo).

UPDATE:

  • Ho bisogno di evitare di fare chiamate alla funzione poiché ho bisogno del codice per essere veloce, quindi valutarle in anticipo come per qui non è fattibile

  • Supponiamo che le funzioni qualcosa ... () abbiano effetti collaterali che sono progettati per aiutare ad aumentare la velocità di calcolo (es: qualcosaC () non dovrebbe essere calcolato se qualcosaB () restituisce true poiché invoca un costo molto elevato funzione)

  • Anche l'antipattern della freccia non risolve il mio caos "ramificazione in" di dover aggiungere più booleani a ogni livello per scendere rapidamente su un ramo di scelta

posta Water 03.03.2017 - 19:28
fonte

3 risposte

3

Non ho la reputazione di fare un commento su JacquesB post.

Suggerisco di suddividere il problema in due problemi minori. 1. Calcola lo stato successivo. 2. Elabora quello stato.

Il calcolo dello stato successivo può essere eseguito in una funzione con più ritorni. Per velocizzare la corsa, controlla le condizioni più frequenti prima delle condizioni meno frequenti.

Al ritorno dalla funzione compute_next_state usa un'istruzione switch per elaborare ogni stato.

    
risposta data 03.03.2017 - 21:28
fonte
2

Se il tuo codice è un FSM dovrebbe essere possibile passare direttamente a uno stato specifico rendendo esplicito lo stato. Per esempio. se hai:

if (state="start" && somethingA()) state = "A";
if (state="A" && somethingB()) state = "B";
if (state="B" && (somethingX() && somethingY()) ...
if (state="B") ...

Quindi puoi ricominciare dall'inizio impostando la variabile di stato sul valore iniziale, ma puoi anche iniziare da qualsiasi stato specifico inizializzando la variabile di stato in qualsiasi altro stato.

Quando si ha un condizionale profondamente annidato (magari spanning di più funzioni annidate), in ogni punto si ha uno stato implicito che indica quali rami sono stati selezionati in corrispondenza di ciascun incrocio. Trasformando questo percorso in un oggetto di stato esplicito, puoi "appiattire" il condizionale (possibilmente in un gigante a livello singolo if-else o switch) in modo da poter passare direttamente a ciascun punto.

    
risposta data 03.03.2017 - 20:53
fonte
1

Potresti essere in grado di utilizzare un'istruzione switch. Se non includi un'interruzione alla fine di ogni blocco, il programma cadrà e le dichiarazioni esecutive dopo i casi successivi.

C'è un buon articolo sulle dichiarazioni switch qui .

    
risposta data 04.03.2017 - 05:05
fonte

Leggi altre domande sui tag