2011-06-20 3 views
5

を逃れることができる私はこのコードのビットを持っている:紙でhttp://www.cs.rice.edu/~taha/publications/journal/dspg04a.pdfF#の引用:変数がスコープ

にMetaOcaml例から変換

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

は、上記の例では、と次が得られることが説明されています(MetaOcaml表記)パラメータ3.<1>.

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

あなたがを見ることができるようには、最も内側のxのみを参照するため、は、x_1x_2などで置き換えられます。fun

しかし、F#ではこれは許可されていません。コンパイル時エラーが発生する: "変数 'x'は引用符で囲まれていますが、スプライスされた式の一部として使用されます。スコープからエスケープする可能性があるため、これは許可されていません。だから問題は、これがどのように変更されてコンパイルされ、MetaOcamlの出力と同じセマンティックを持つことができるかということです。

コメントの更新:パワーパックを使用して、実際に見積もりを評価します。しかし、エラーはコンパイル時に発生するため、これと何も関係がないとは思わない。今のところQuotationEvaluationは機能します。しかし、私はそれが最も効率的な実装ではないかもしれないことを知っています。 Tomas'の答えに

更新: 私は本当にxがグローバルになりたくない、または範囲をエスケープします。しかし、私は望むと同等です

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

見積もりと。あなたの答えは(h 3 <@ 1 @>).Eval() = 4です。ここで上記は、h 3 1 = 7です。そして、ここでは、私は7を答えたいと思っています。

+0

MeyaOCamlは分かりませんが、実際のF#コードではラムダ引用の変数を使用しようとしているようです。引用は実際のF#コードではありません。そのため、それらを評価することはできません(完全ではないLINQエバリュエーターを除いて)。 –

答えて

6

F#の引用構文は、スコープをエスケープする可能性のある変数をサポートしていないため、Expr操作を使用して明示的にツリーを構築する必要があります。このような何かはトリックを行う必要があります。

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))) 

しかし、これは非常に人工的な例としては、(F#で使用できないMetaOCaml、内の変数キャプチャを実証すること)です。それはちょうど(2 + (1 + ...))のような表現を生成します。私はまた、F#の相場ではこの制限accrossに来て、これがサポートされていた場合、それはいいだろう

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

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

あるいはさらに良い:あなたはこのような何かを書くことで同じ結果を得ることができます。しかし、私はそれが実際には大きな問題ではないと考えています。なぜなら、F#引用は段階的メタプログラミングのために使われないからです。これらは、コードを生成するよりも既存のF#コードを分析する方が便利です。

+0

あなたは有能な男のように見えます:)しかし、これは完全に私が望むものではありません。私は私の質問で十分に明確ではなかったかもしれないので、2秒で質問の更新があります。 –

+0

@lasseespeholt:「Var.Global」の代わりに「新しいVar」を使うようにバージョンを変更しました(間違っていました)。これで、すべての反復で新しい変数が宣言されるはずです(同じ_name_を持ちますが、_different_変数になります)。 –

+0

@Tomasありがとう:)期待どおりに動作します。しかし、私はMetaOcamlにフォールバックすると思います。私はそれをマルチステージプログラミングに使用したいからです。 –