2016-07-25 3 views
1

区別されたユニオンの大文字小文字の値をコピーするにはどうすればよいですか?区別されたユニオンの大文字小文字の値をコピーするにはどうすればよいですか?

次のコードは、いくつかの重複があります。

let move (direction:Direction) (checker:Checker) = 
    match checker with 
    | Red xy -> Red { xy with X=2; Y=2 } 
    | Black xy -> Black { xy with X=2; Y=2 } 

を具体的に、私はちょうどそのレコードの値を設定するためにチェッカーの種類を指定する必要はありません。したがって、チェッカーが赤か黒かどうかは気にしません。チェッカーケースの値をコピーしてその位置を更新するだけです。私はむしろどうなる

このような何か:

let move (direction:Direction) (checker:Checker) = 
    match checker with 
    | _ xy -> _ { xy with X=2; Y=2 } 

は、ここに私のテストです:

[<Test>] 
let ``move checker``() = 
    Black { X=1; Y=1 } |> move NorthEast 
         |> should equal (Black { X=2; Y=2 }) 

付録:

module Test3 

open NUnit.Framework 
open FsUnit 

type Position = { X:int; Y:int } 
type Checker = | Red of Position | Black of Position 

type Direction = 
    | NorthEast 
    | NorthWest 
    | SouthEast 
    | SouthWest 

(* Functions *) 
let move (direction:Direction) (checker:Checker) = 
    match checker with 
    | Red xy -> Red { xy with X=2; Y=2 } 
    | Black xy -> Black { xy with X=2; Y=2 } 

[<Test>] 
let ``move checker``() = 
    Black { X=1; Y=1 } |> move NorthEast 
         |> should equal (Black { X=2; Y=2 }) 
+3

あなたのデータタイプたとえば、あなたはあなたの例それぞれの位置を更新する方法を知っているあなたのユニオン型で定義された、特殊なマップ機能を持たせることができます内側にあります。これがあなたのユースケースであれば、チェッカーを '{pos:Position; color:Color} 'を選択すると、色に触れずに位置を更新できます。 –

+0

ああ...意味があります。ところで、私の設計上の問題にかかわらず、組合のケース値はコピーできますか? –

答えて

6

あなたが求めていることは、反射のトリッキーなことなしにF#で行うことはできません。

ここでは常識的な説明:ユニオンのケースがまったく同じデータを持つことは非常にまれです。これが当てはまる場合、おそらく、連合ケース自体がデータをマークするために使用される単なる「タグ」であることを示しているため、「周囲」ではなくデータの「横」にエンコードする必要があります。そのような組合のタイプはまれな例外であるため、それらをサポートする言語機構を発明することは意味がありません。

この合理化は、特定のケースにうまく当てはまります。チェッカーの色は、「ラッピング」するのではなく、位置の「横」にある「タグ」です。チェッカーを{ pos: Position; color: Color }と定義すると、色に触れずに位置を更新することができます。

+0

技術的には、同じデータを持つUnionのケースでは、リフレクションなしで実行可能です(let(|))。 CheckerCtor |)=関数赤_ - >赤|このようにして使用することができます(希望の場合はそれぞれのケースに関連するデータを抽出することができますが、実際には複雑です) – Sehnsucht

+0

これは似ています(いくつかの意味で)scrwtpが彼の答えに書いたものに、私の反対も同じようになるでしょう。 –

+0

確かな事;私はそれが使用されるべきであるとは決して言わない。ちょうど「反射だけで済む」ということについての答えはあなたの答えに誤解を与えています。 – Sehnsucht

4

私は "タグは、それを包み込むのではなく位置の隣に置くべき"と思っています。確かに、この例はあまり単純すぎるかもしれませんが、あなたのユニオンケースの中には、透過的な方法で操作したいデータを共有するケースが非常に有効です。

標準のFPパターンは、そのタイプにマップ機能を持たせることです。

type Checker = 
    | Orange of Position 
    | Blue of (Position * DateTime) 
    | Bacon of bool 
    member this.MapPosition (f: Position -> Position) = 
     match this with 
     | Orange pos  -> Orange (f pos) 
     | Blue (pos, dt) -> Blue (f pos, dt) 
     | Bacon x  -> Bacon x 

そして、このようにそれを使用します:

let move (direction:Direction) (checker:Checker) = 
    checker.MapPosition(fun _ -> { X=2; Y=2 }) 
+0

それはスコットが求めていたものではありません。彼はそれをするために魔法の言語機能を望んでいて、それは存在しません。もちろん、ユーティリティ機能ですべての醜さを隠すことができれば、何でもできます。しかし、醜さは消えず、隠れているだけです。 –

+0

また、すべてのケースで実際に同じデータが実際には存在しないことに注意してください。これは私が記述していた「タグ」状況ではありません。 –

+0

@FyodorSoikin: "魔法の言語機能"と "コード全体にマッチをコピー・ペーストする"の間には、さまざまなものがあります。 OPが探していたのは、コードの重複を減らす方法でした。この種の問題に対する一般的で構造化されたアプローチを提案しました。私はここで醜さを見ません。 – scrwtp

関連する問題