Come interpreto questo algoritmo postfisso da destra a sinistra?

1

Sto cercando di implementare questo algoritmo di valutazione da destra a sinistra di un'espressione postfissa, ma non riesco a farlo funzionare.

for each token in the reversed postfix expression:
  if token is an operator:
    push token onto the operator stack
    pending_operand ← False
  else if token is an operand:
    operand ← token
    if pending_operand is True:
      while the operand stack is not empty:
        operand_1 ← pop from the operand stack
        operator ← pop from the operator stack
        operand ← evaluate operator with operand_1 and operand
    push operand onto the operand stack
    pending_operand ← True
result ← pop from the operand stack

Da wikipedia .

Ecco come vengono illustrati i passaggi:

15 7 1 1 + − ÷ 3 × 2 1 1 + + − =
15 7 1 1 + − ÷ 3 × 2     2 + − =
15 7 1 1 + − ÷ 3 ×         4 − =
15 7     2 − ÷ 3 ×         4 − =
15         5 ÷ 3 ×         4 − =
             3 3 ×         4 − =
                 9         4 − =
                             5

Non ho davvero capito come questo segue dall'algoritmo. Continuo a ottenere la risposta sbagliata cercando di valutare l'espressione 15 7 1 1 + − ÷ 3 × 2 1 1 + + − (dovrebbe essere 5 ). Ho passato ore a provare a farlo funzionare in assembly e ho provato a passarlo manualmente, ma continuo a ricevere la risposta sbagliata. Penso che parte di if si trovi in operate(operand_1, operand) , esclusa. Comunque, ho buttato insieme questo pezzo di JavaScript per mostrare la mia interpretazione dell'algoritmo, poiché è molto più chiaro di assemblaggio.

const   rpn = [15, 7, 1, 1, '+', '-', '/', 3, '*', 2, 1, 1, '+', '+', '-'];
const   operator_Stack = [];
const   operand_Stack = [];
let     pending = false;

for (i = rpn.length - 1; i >= 0; i--) {
    const   token = rpn[i];
    if (typeof token === "string") {
        operator_Stack.push(token);
        pending = false;
    } else {
        let operand = token;
        if (pending) {
            while (operand_Stack.length > 0) {
                let operand_1 = operand_Stack.pop();
                let operator = operator_Stack.pop();
                let expr = operand + " " + operator + " " + operand_1;
                console.log(expr);
                operand = eval(expr);
            }       
        }
        operand_Stack.push(operand);
        pending = true;
    }
}
console.log("The expression evaluates to: " + operand_Stack.pop());

Questo valuta la seguente espressione nel seguente ordine:

"1 + 1"
"2 + 2"
"1 + 1"
"2 - 3"
"-1 / 4"
"7 * -0.25"
"15 - -1.75"

Le prime tre valutazioni sembrano corrette. Poi le cose iniziano a sbagliare.

Come un albero binario 15 7 1 1 + − ÷ 3 × 2 1 1 + + − assomiglierebbe a questo

            [-]
           /   \
         [*]    [+]
        / \    /   \
      [/] [3] [2]  [+]
      / \          /  \
   [15] [-]      [1]  [1]
        / \
     [7]  [+]
         /   \
       [1]   [1]            

L'ordine corretto di valutazione dovrebbe essere:

1 + 1
2 + (1 + 1)
1 + 1
7 - (1 + 1)
15 / (7 - 2)
3 * (15 / 5)
9 - 4

Per me, il mio codice JavaScript implementa l'algoritmo come indicato. Eppure, ovviamente, non è corretto. Come vedo, ci sono due possibilità, l'algoritmo è sbagliato o, più probabilmente, la mia interpretazione è. Il problema è che non riesco a capire quale dei due sia.

Che cosa mi manca?

    
posta CervEd 18.01.2018 - 02:45
fonte

1 risposta

2

Hai un programma un po 'complicato, che produce un risultato inaspettato. Sembra funzionare correttamente nei primi passi, ma da qualche parte va storto. Dove? Per prima cosa fai in modo che riduca l'input nel modo più semplice possibile, il che produce comunque un errore .

Rimuovendo manualmente la sottoespressione, ho finito con:

rpn = [7, 2, '-'];

Questo è 7 - 2 e dovrebbe ovviamente risultare in 5 . Ma il programma produce effettivamente -5 . Una possibile spiegazione potrebbe essere che inverte effettivamente gli operandi, poiché 2 - 7 = -5. Questa ipotesi è facile da verificare ora utilizzando la divisione. Per esempio. [10, 2, '/'] è 10/2 che è 5, ma se lo eseguiamo otteniamo 0,2 che capita di essere 2/10. Quindi chiaramente gli operandi sono commutati. Ora guarda la riga nell'algoritmo:

operand ← evaluate operator with operand_1 and operand

Questa linea è forse ambigua poiché non specifica esplicitamente l'ordine degli operandi. Ma operand_1 è quella che viene prima spuntata che significa l'ultima spinta. In altre parole, in [7, 2, '-'] , operand è 2 e operand_1 è 7. Nella tua implementazione hai operand_1 <operator> operand che è al contrario di quanto ti aspetti.

Il motivo per cui non vedi l'errore nelle prime tre iterazioni è che l'ordine degli operandi non ha importanza per + .

Dopo aver controllato con gli operandi invertiti, sembra che ci sia un secondo problema che riguarda l'algoritmo stesso. Considera questo input:

[15, 7, 2, '-', '/', 3, '*']

Questo dovrebbe corrispondere a 15 / (7-2) * 3 che dovrebbe essere 9, ma il programma restituisce 25. Sembra che valuti come (7-2) / 3 * 15.

Secondo l'algoritmo come detto, dopo aver risolto 7 2 - , dovrebbe quindi continuare a risolversi dallo stack finché lo stack degli operandi non è vuoto, e solo allora continuare ad elaborare 15. Poiché 3 è sullo stack degli operandi a questo punto, ottieni 5/3 che è sbagliato per quanto posso dire.

Wikipedia non fornisce un riferimento per l'algoritmo, ma guardando la cronologia delle modifiche della pagina, sembra che qualcuno abbia modificato la riga del pugno da "espressione prefisso invertita" a "espressione postfix invertita". Forse c'è stata una certa confusione perché l'inversione di una notazione di prefisso nel suo complesso è non uguale a postfix aka reverse polish.

Considera questa notazione infissa:

(2+2)/(7-2)*3

In notazione prefisso (aka polacco):

* / + 2 2 - 7 2 3

Nella notazione postfix (aka reverse Polish):

2 2 + 7 2 - / 3 *

Si noti che questo è diverso dal semplice inversione della notazione del prefisso nel suo complesso! Ma Wikipedia sembra mescolare questo. Ho anche notato che l'algoritmo specificato da Wikipedia per la risoluzione del prefisso notazione è lo stesso dell'algoritmo da sinistra a destra per la notazione postfissa . Ma poiché questi non sono gli stessi mostrati sopra, credo che l'algoritmo sia sbagliato nel secondo contesto.

La notazione di smussatura inversa non è realmente adatta all'elaborazione da destra a sinistra. Se si desidera utilizzare l'elaborazione da destra a sinistra, è consigliabile prendere in considerazione l'uso della notazione del prefisso come input.

    
risposta data 18.01.2018 - 14:41
fonte

Leggi altre domande sui tag