2017-09-26 4 views
3

私はキーがさまざまな形を取るかもしれないマップを持っています(私は "シェイプ"という言葉をゆるやかに使っています)。これを処理するために、私は、判別組合(DU)を作成しました:同じタイプであるが形状が異なるオブジェクトを扱う慣習的な方法は何ですか?

type AuxDataKey = 
    | OneString   of string 
    | TwoStrings  of string * string 
    | OneStringInt  of string * int 
    | TwoStringsInt  of string * string * int 
    | OneStringTwoInts of string * int * int 
    | TwoStringsTwoInts of string * string * int * int 

type AuxData = Map<AuxDataKey,AuxDataItem> 

キーは、この方法で作成することができます。

let key = AuxDataKey.TwoStringsInt("Vol", ticker, count) 

情報はオンザ・作成したキーから取得することができますフライ:

let vols = auxData.[AuxDataKey.TwoStringsInt("Vol", ticker, count)] 

これは機能しますが、私はかなり煩わしいです。

type StringInt = String of string | Int of int 

、その後、マップキーは、このようなオブジェクトのリストもあります

1つの選択肢は、別のDUを作成することです。マップキーのDUを作成する必要はなく、StringInt DUははるかに簡単です。一方、コンパイラはリストを受け入れることができるため、例えば、最初の要素がIntで、2番目がStringの場合は、AuxDataKey DUのいずれの場合にも適合しないので、この方法は安全性が低下します。もっと良い選択肢があるかもしれないので、私は実際にこれを試していません。

mustが同じタイプであるが形状が異なるオブジェクトを扱う慣用方法はありますか?

+1

あなたの現在のソリューションについて、まったく気に入らないものは何ですか?あなたは理想的な世界でどのように見えるでしょうか? –

+0

私には大きな苦情はありません。私が新しいケース(例えば、ThreeStringsTwoInts)を考え出すと、私はDUコードを変更しなければならないということです。しかし、それはDUが働く方法であり、それはまた良い面(安全)を持っています。私はその質問の中で一つの選択肢について言及しましたが、私が考え出したより良い選択肢があるかもしれないと思いました。理想的な言葉では、 'OneStringOneInt'を指定することなく' AuxDataKey( "Vol"、3) 'と書くだけです。それを考えてみてください。おそらく、複数のコンストラクタを持つクラスで行うことができます。 – Soldalma

+0

それはまったく慣用的なことではありませんが、オーバーロードされた 'Item'メンバーでIDictionaryを実装するクラスを作ることができると思います。それからあなたは 'auxData。[(" Vol "、ticker、count)]'と言うことができます。正直なところ、私はおそらくキーの種類ごとに別々の 'Map'を持っているでしょう。明らかに、コンパイル時にはどちらを使うべきかを知っています。そして、すべての値(すべての 'Map'sを連結したものの値であることになります)または典型的な' Map.map'(これを適用する必要があります)を取得する必要がある場合は、いくつかのヘルパー関数を追加してください機能をすべてのマップと一緒にマップする) –

答えて

5

あなたのやり方はかなり慣れています。あなただけ書くことができますので、あなたが、DUのケースを参照するときに型名AuxDataKeyを前に付ける必要がないことに注意してください:

let key = TwoStringsInt("Vol", ticker, count) 

意味のある部分にあなたのタイプを分解することが可能かもしれないが、それは難しい伝えるためにコンポーネントの部品が実際に何を意味するか知らずにこれは、繰り返しのいくつかを取り除くことができます。たとえば、あなたにこれを与えるだろう、すべてのケースに共通している文字列、のいずれかを引き出す:

type SubKey = 
    | A of string 
    | B of int 
    | C of string * int 
    | D of int * int 
    | E of string * int * int 

type AuxDataKey = 
    { Key : string 
     SubKey : SubKey } 

いくつかの他の言語には、労働組合の例は、自動的に別のものを値に基づく型システムによって推測されている機能を持っています返されます。私はTypeScriptがこれを行うことができると信じています。そして、OCamlはその多相変種と似たものを持っています。しかし、F#は完全に名目上型付けされています。つまり、各DUケースは、使用する前に定義して名前を付ける必要があります。

1

私は、あなたが本当にしたいと思いますが、このようなものであることを理解:

type AuxDataKey = 
    | Vol of string * int 
    | AnotherKey of string * int * int * string 
    | ... 

が、非常に多くの場合、これは無意味な運動のように感じていることがありますか?

あなたの質問が私に読まれる方法、あなたはexpression problemの間違った側にいるという苦痛を感じています。あなたは異種のタイプのオープンユニバースを持っており、それらを実現可能にするために(閉じた)弁別されたユニオンを使用するような形のパターンを見つけることを試みています。

F#を使用すると、言語のオブジェクト指向の機能を使用して側面を簡単に切り替えることができます。以前使用していたパターンは、共通の基本タイプ(インターフェースまたは抽象クラスのいずれか)を継承するオープンな階層構造を作成し、部分的にアクティブなパターンを使用してそれらを補強し、 F#。、

let x = { arg1 = "test"; arg2 = 42 } :> IAuxDataKey 

match x with 
| VolKey (arg1, arg2) -> printfn "arg1: %s, arg2: %d" arg1 arg2 
| _ -> printfn "unknown" 

IAuxDataKeyはここだけマーカーインターフェイスですが、あなたはそれがあなたのコンテキストで理にかなっている場合が一般的なメンバーを追加する(例えば、解析やメソッドを作成することができます。

type IAuxDataKey = interface end 

type VolKey = 
    { 
     arg1: string 
     arg2: int 
    } 
    interface IAuxDataKey 

let (|VolKey|_|) (value: IAuxDataKey) = 
    match value with 
    | :? VolKey as key -> Some (key.arg1, key.arg2) 
    | _ -> None 

は、このようにそれらを使用し、キーが均質なソースから来た場合)。

あなたがここで失うものは網羅的なマッチを持つ能力です。キャッチオールケースが必要です。しかし、私はそれがあなたのユースケースでは大した問題ではないと思っています。

+0

私の問題は、あまりにも多くの異なるケースがあったというわけではありませんでした。あなたの 'let x = ...'行のコードは、私の 'let key = ...'のものより単純ではありません。特に、クイックブラウンフォックスがタイプ名 'AuxDataKey'を落とすよう提案した後です。私はオブジェクトを分解する必要はありませんが、実際に別のスコープで再構築する必要があります。だから私はあなたのアイデアを保存します。これは、別の時間/問題のために、かなり面白いかもしれません。 – Soldalma

関連する問題