1

F# でインタープリターを作成しています。私は賢く、プリミティブ演算子の解釈を関数呼び出し (この場合はリダクション) として一般化しようとしています。

これがアイデアです:

let reduce fx values =
    Array.reduce values fx

let primitivesEnv = 
    let r = Dictionary<string,'T -> 'T -> 'T>()
    r.["int_+"] <- reduce (fun(l:int, r:int) -> l + r)
    r.["int_-"] <- reduce (fun(l:int, r:int) -> l - r)
    r

だから私は後でこれを行うことができます:

env.["int_+"]([| 1, 2 |])

もちろん、型チェッカーはこれを拒否します

警告 FS0064: この構成により、コードは型注釈で示されるよりも一般的ではなくなります。型変数 'T は型 ''a -> 'a -> 'a' になるように制約されています。エラー FS0001: タイプが一致しません。a ('a -> 'a -> 'a) -> ('a -> 'a -> 'a) -> 'a -> 'a -> 'a を期待しているが、a ('a -> 'a -> 'a) -> 'a ''a' と '('a -> 'a -> 'a) -> 'a -> 'a -> 'a' を統合すると、結果の型は無限になります。

PD: 単純なインタープリターとしてこれを行う方法は知っていますが、それぞれに MATCH を作成することなく、汎用的な方法で数十のメソッドを構築できるソリューションを構築しようとしています。

4

1 に答える 1

1

まず、コードにいくつかの問題があります。の辞書に型注釈を追加しましたが、配列を指定して削減を評価しようとしているため'T -> 'T -> 'T、型の関数を返す必要があるようです。'T[] -> 'T

また、[| 1, 2 |]は 1 つのタプルの配列です。次のような複数の値の配列が必要です。[| 1; 2 |]

私はあなたのコードを次のように修正しました:

let reduce values =
    Array.reduce values

let primitivesEnv = 
    let r = System.Collections.Generic.Dictionary<string,'T[] ->'T>()
    r.["int_+"] <- reduce ((+) : int -> int -> int)
    r.["int_-"] <- reduce ((-) : int -> int -> int)
    r

let result = primitivesEnv.["int_+"] [| 1; 2 |]

残念ながら、これで問題は終わりではありません。

ディクショナリは型であると言ったかもしれませんが'T[] ->'T、そうではありません。ディクショナリの唯一の有効な型は です。int[] -> int最初のint -> int -> int型注釈はその制約を作成します。その型注釈を省略'Tするintと、int[].

型パラメーター'Tは、常に何らかの方法で固定型に解決する必要があります。これは、何かを使用できるワイルド カードではありません。

これはfloatint. ディクショナリを使用する場合の唯一のオプションは、1 つのタイプを選択するか、タイプ セーフの一部を破棄して、実行時に特定のタイプを解決することです。

これを行うための最も簡単な変更は、さまざまなリダクション タイプを記述するためにいくつかのユニオン ケースを作成することです。

type Reduction =
    |IntReduction of (int[] -> int)
    |FloatReduction of (float[] -> float)

Dictionary<string, Reduction>次に、 の代わりに を作成しますDictionary<string,'T[] ->'T>


F# でインタープリターを作成するというより一般的な問題になると、解釈したいミニ言語の式と構造を記述する判別共用体の構造化セットを作成することから始めます。これは抽象構文ツリー ( AST)。

run次に、ツリーをたどって AST で記述されたすべての計算を実行する関数を定義できます。

また、パーサー コンビネーター ライブラリ (FParsec をお勧めします) を使用して、構造化されたテキストを解析して、上記の手順で定義したユニオン ケースの抽象構文ツリーにします。

Phillip Trelford は、単純な C# AST に FParsec を使用してこれを行う方法の例をオンラインで公開しています : http://www.fssnip.net/lf .

于 2015-12-30T18:16:15.837 に答える