2011-06-20 13 views
5

ho questo pezzo di codice:F # citazioni: variabile può sfuggire portata

let rec h n z = if n = 0 then z 
       else <@ (fun x -> %(h (n - 1) <@ x + %z @>)) n @> 

convertito da un esempio MetaOcaml in http://www.cs.rice.edu/~taha/publications/journal/dspg04a.pdf

Nel documento viene spiegato che l'esempio precedente produrrà seguito con i parametri 3 e .<1>. (in MetaOcaml notazione):

.<(fun x_1 -> (fun x_2 -> (fun x_3 -> x_3 + (x_2 + (x_1 + 1))) 1) 2) 3>. 

Come si può vedere il x viene sostituito da x_1, x_2 ecc. Perché lo x si riferirebbe in altro modo solo allo nella parte interna dello fun.

Ma in F # non è consentito. Ricevo l'errore in fase di compilazione: "La variabile 'x' è associata a una quotazione ma viene utilizzata come parte di un'espressione splicata. Ciò non è consentito poiché potrebbe sfuggire al suo ambito." Quindi la domanda è: come può essere cambiato così da compilare e avere la stessa semantica dell'output di MetaOcaml?

Aggiornamento al commento: Uso il PowerPack per valutare effettivamente l'offerta. Ma non penso che questo abbia nulla a che fare con questo perché l'errore è in fase di compilazione. Finora QuotationEvaluation funziona. Tuttavia, so che potrebbe non essere l'implementazione più efficiente.

Update per Tomas' risposta: Io davvero non si vuole il x essere globale, o per sfuggire la portata. Ma voglio l'equivalente a

let rec h n z = if n = 0 then z 
       else (fun x -> (h (n - 1) (x + z))) n 

con citazioni. La tua risposta dà (h 3 <@ 1 @>).Eval() = 4 dove il rendimento sopra è h 3 1 = 7. E qui, voglio che 7 sia la risposta.

+0

Non conosco MeyaOCaml, ma sembra che si stia tentando di utilizzare una variabile da una quotazione lambda nel codice F # reale. Le citazioni non sono un codice F # reale, e per questo motivo non è possibile valutarle (tranne con il programma di valutazione LINQ, che non è perfetto). –

risposta

6

La sintassi dell'offerta F # non supporta le variabili che potrebbero potenzialmente sfuggire dall'ambito, pertanto sarà necessario costruirlo esplicitamente utilizzando le operazioni Expr. Qualcosa del genere dovrebbe fare il trucco:

open Microsoft.FSharp.Quotations 

let rec h n (z:Expr<int>) = 
    if n = 0 then z     
    else 
    let v = new Var("x", typeof<int>) 
    let ve = Expr.Var(v) 
    Expr.Cast<int> 
     (Expr.Application(Expr.Lambda(v, h (n - 1) <@ %%ve + %z @>), 
          Expr.Value(n))) 

Tuttavia, questo è l'esempio del tutto artificiale (per dimostrare l'acquisizione variabile MetaOCaml, che non è disponibile in F #). Genera solo espressioni come (2 + (1 + ...)). È possibile ottenere lo stesso risultato scrivendo qualcosa di simile:

let rec h n (z:Expr<int>) = 
    if n = 0 then z     
    else h (n - 1) <@ n + %z @> 

O ancora meglio:

[ 1 .. 4 ] |> List.fold (fun st n -> <@ n + %st @>) <@ 0 @> 

Ho anche mai incontrato questa limitazione in F # citazioni e sarebbe bello se questo è stato sostenuto. Tuttavia, non penso che sia un grosso problema nella pratica, perché le citazioni di F # non sono usate per la meta-programmazione a fasi. Sono più utili per analizzare il codice F # esistente piuttosto che per generare codice.

+0

Sembri un ragazzo competente :) Ma questo non è interamente ciò che voglio. Potrei non essere stato abbastanza chiaro nella mia domanda, quindi ci sarà un aggiornamento della domanda in 2 secondi. –

+0

@lasseespeholt: Ho cambiato la versione per usare 'nuova Var' invece di' Var.Global' (che era semplicemente sbagliato). Questo dovrebbe ora dichiarare una nuova variabile in ogni iterazione (avranno lo stesso _name_, ma saranno _differenti_ variabili). –

+0

@Tomas Grazie :) funziona come previsto. Tuttavia, suppongo che ricadrocherò su MetaOcaml perché volevo usarlo per la programmazione multi-stage. –

Problemi correlati