Come implementare la valutazione pigra di if ()


10

Attualmente sto implementando un valutatore di espressioni (espressioni a riga singola, come formule) basato su quanto segue:

  • l'espressione inserita viene tokenizzata per separare letterali booleani, numeri interi, decimali, stringhe, funzioni, identificatori (variabili)
  • Ho implementato l'algoritmo Shunting-yard (leggermente modificato per gestire le funzioni con un numero variabile di argomenti) per sbarazzarsi della parentesi e ordinare agli operatori con una discreta precedenza in un ordine postfisso
  • il mio shunting-yard produce semplicemente una coda (simulata) di token (tramite un array, il mio linguaggio Powerbuilder Classic può definire oggetti, ma ha solo array dinamici come memoria nativa - non un vero elenco, nessun dizionario) che valuto sequenzialmente con un semplice stack machine

Il mio valutatore funziona bene, ma mi manca ancora un if()e mi chiedo come procedere.

Con la mia valutazione postfissa e basata sullo stack di shunt, se aggiungo if()come un'altra funzione con parti vere e false, un singolo if(true, msgbox("ok"), msgbox("not ok"))mostrerà entrambi i messaggi mentre vorrei mostrarne solo uno. Questo perché quando devo valutare una funzione, tutti i suoi argomenti sono già stati valutati e messi in pila.

Potresti darmi un modo per implementare if()in modo pigro?

Ho pensato di elaborarli come una specie di macro, ma all'inizio non ho ancora la valutazione delle condizioni. Forse ho bisogno di usare un altro tipo di struttura che una coda per mantenere separatamente la condizione e le espressioni vero / falso? Per ora l'espressione viene analizzata prima della valutazione, ma ho anche in programma di memorizzare la rappresentazione intermedia come tipo di espressione precompilata per la valutazione futura.

Modifica : dopo alcune considerazioni sul problema, penso di poter costruire una rappresentazione ad albero della mia espressione (un AST invece di un flusso di token lineare), dal quale potrei facilmente ignorare uno o un altro ramo del mio if().

Risposte:


9

Ci sono due opzioni qui.

1) Non implementare ifcome funzione. Rendilo una funzionalità linguistica con semantica speciale. Facile da fare, ma meno "puro" se vuoi che tutto sia una funzione.

2) Implementa la semantica "call by name", che è molto più complicata, ma consente alla magia del compilatore di occuparsi del pigro problema di valutazione mantenendo ifuna funzione anziché un elemento di linguaggio. Va così:

ifè una funzione che accetta due parametri, entrambi dichiarati "per nome". Quando il compilatore vede che sta passando qualcosa a un parametro per nome, cambia il codice da generare. Invece di valutare l'espressione e passare il valore, crea una chiusura che valuta l'espressione e la passa invece. E quando si richiama un parametro per nome all'interno della funzione, il compilatore genera codice per valutare la chiusura.


Non sono sicuro, ma "chiusura" dovrebbe essere "thunk"? Hmm, forse no; ho appena guardato la pagina di Wikipedia: "un thunk è una chiusura senza parametri".

Quando dici "chiama per nome" ti riferisci a livello globale? In alternativa al call-by-name globale è solo l'implementazione di un tipo di chiusura, quindi la funzione if prende solo 3 chiusure e ne valuta due (condizione e quindi o altro), ma non tutto deve essere riconosciuto come una chiusura come la chiamata completa per- semantica del nome
Jimmy Hoffa

@Matt: Il termine "thunk" può significare molte altre cose nel contesto della programmazione e "chiusura senza parametri" non è la prima che mi viene in mente quando la sento. La "chiusura" è molto più chiara.
Mason Wheeler

1
@JimmyHoffa: quando dico "chiama per nome", mi riferisco a uno stile specifico di impostazione di un argomento di funzione, che dovrebbe essere facoltativo. Proprio come molte lingue esistenti ti permetteranno di scegliere di passare un parametro per valore o per riferimento, per questo scenario devi scegliere di passare per nome.
Mason Wheeler,

Mentre il tuo suggerimento sulla semantica "chiama per nome" mi ha mostrato alcuni punti interessanti, è un po 'eccessivo per il mio valutatore che non è un compilatore completo, poiché le mie chiamate di funzione sono a riga singola (pensa alle formule MS-Excel). Sto pensando di poter aggiungere un passaggio dopo l'accodamento dei token eseguendo una pseudo-valutazione dello stack per dedurre l'albero chiamante. Dovrebbe essere più facile sapere dall'albero i rami da scartare.
Seki,

3

Invece della funzione con la firma:

object if(bool, object, object)

Dagli la firma:

object if(bool, object function(), object function())

Quindi la tua iffunzione chiamerà la funzione appropriata in base alla condizione, valutandone solo una.


1

È abbastanza facile, se compili tutto pigramente. È necessario disporre di alcuni mezzi per vedere se un valore è già valutato o se necessita di maggiore evlauazione.

Quindi puoi fare quanto segue: Se è un valore letterale o variabile (hai quelli ?, cioè i nomi delle funzioni?), Spingilo nello stack. Se si tratta di un'applicazione di una funzione, compilarla separatamente e spingere il punto di ingresso nello stack.

L'esecuzione di un programma è quindi semplicemente in loop fino a quando non viene valutata la parte superiore dello stack e non una funzione. Se non viene valutato o una funzione, chiamare il codice in cima a cui punta lo stack.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.