6

リフレクションを使用して、F# のいくつかの判別共用体の値を列挙しています (たとえば、F# で判別共用体を列挙するにはどうすればよいですか? )。リフレクションを使用して取得した値を使用して、列挙している識別されたユニオンから構成されるさまざまなレコード タイプを生成したいのですが、タイプ UnionCaseInfo を実際のユニオン ケースにキャストする方法がわかりません。そのようなキャストを実行することは可能ですか? 以下のコードは、私がやろうとしていることを正確に表しています (判別共用体の値は異なり、変数名も異なります)。列挙を使用できることは承知していますが、差別共用体の代わりに列挙を使用しないことをお勧めします。

open System
open Microsoft.FSharp.Collections
open Microsoft.FSharp.Reflection

let GetUnionCaseName (x:'a) = 
    match FSharpValue.GetUnionFields(x, typeof<'a>) with
    | case, _ -> case.Name  

type shape =
    | Square
    | Circle
    | Triangle
    | Other

type color =
    | Black
    | Red
    | Blue
    | Green
    | White

type coloredShape = { Shape: shape; Color: color }

let shapeCases = FSharpType.GetUnionCases typeof<shape>
let colorCases = FSharpType.GetUnionCases typeof<color>

let boardOfRelevantPossibilities = Microsoft.FSharp.Collections.Array2D.init<coloredShape> 5 3 (fun x y -> {Shape = Other; Color = Black})

let OtherShape = GetUnionCaseName(shape.Other)
let rand = Random()

for shapeCase in shapeCases do
    // Is there a way to do the following comparison this without using string comparisons
    if not (shapeCase.Name.Equals OtherShape) then
        for colorCase in colorCases do
            let mutable addedToBoard = false

            while not addedToBoard do
                let boardRowIndex = rand.Next(0,4)
                let boardColumnIndex = rand.Next(0,2)

                if boardOfRelevantPossibilities.[boardRowIndex,boardColumnIndex].Shape.Equals shape.Other then
                    addedToBoard <- true

                    // I want to utilize colorCase instead of other and shapeCase instead of black
                    boardOfRelevantPossibilities.[boardRowIndex,boardColumnIndex] <- {Shape = Other; // Shape should be determined by shapeCase instead of Other  
                        Color = White } // Color should be determined by colorCase instead of White

Console.ReadKey() |> ignore

コードを次のようにリファクタリングしました。

open System
open Microsoft.FSharp.Collections
open Microsoft.FSharp.Reflection

let allUnionCases<'T>() =
    FSharpType.GetUnionCases(typeof<'T>)
    |> Array.map (fun case -> FSharpValue.MakeUnion(case, [||]) :?> 'T)

type shape =
    | Square
    | Circle
    | Triangle
    | Other

type color =
    | Black
    | Red
    | Blue
    | Green
    | White

type coloredShape = { Shape: shape; Color: color }

let shapeCases = FSharpType.GetUnionCases typeof<shape>
let colorCases = FSharpType.GetUnionCases typeof<color>

let numberOfRows = 5
let numberOfColumns = 3
let boardOfRelevantPossibilities = Microsoft.FSharp.Collections.Array2D.init<coloredShape> numberOfRows numberOfColumns (fun x y -> {Shape = Other; Color = Black})

let rand = Random()

for shapeCase in allUnionCases<shape>() do
    // No string comparison anymore
    if shapeCase <> shape.Other then
        for colorCase in allUnionCases<color>() do
            let mutable addedToBoard = false

            while not addedToBoard do
                let boardRowIndex = rand.Next(0,numberOfRows)
                let boardColumnIndex = rand.Next(0,numberOfColumns)

                if boardOfRelevantPossibilities.[boardRowIndex,boardColumnIndex].Shape.Equals shape.Other then
                    addedToBoard <- true
                    // utilizing colorCase and shapeCase to create records to fill array
                    boardOfRelevantPossibilities.[boardRowIndex,boardColumnIndex] <- {Shape = shapeCase; Color = colorCase } 


printfn "%A" boardOfRelevantPossibilities
Console.ReadKey() |> ignore

この新しいリファクタリングには、識別共用体を介して列挙するためのリフレクションが組み込まれていますが、それらの識別共用体で構成されるさまざまなレコード タイプを生成できます。また、2 つの off-by-one エラーを検出しました。これらは、リファクタリングされたコードで修正されています。

4

1 に答える 1

11

正直なところ、リフレクションによって作成するよりも、すべてのユニオン ケースを静的メソッドで手動で列挙することを好みます。

@John が言ったように、 FSharpValue.MakeUnionを使用してもう 1 つの手順が必要です。

let allUnionCases<'T>() =
    FSharpType.GetUnionCases(typeof<'T>)
    |> Array.map (fun case -> FSharpValue.MakeUnion(case, [||]) :?> 'T)

whileループでは、完全修飾名のない共用体 ( ) の代わりに使用する必要があります=Equalsshape.Other

/// Use type params for clarity; type inference should work fine without them
for shapeCase in allUnionCases<shape>() do
    for colorCase in allUnionCases<color>() do
        let mutable addedToBoard = false
        while not addedToBoard do
            let r = rand.Next(0,4)
            let c = rand.Next(0,2)
            if boardOfRelevantPossibilities.[r, c].Shape = Other then
                addedToBoard <- true
                boardOfRelevantPossibilities.[r, c] <- { Shape = shapeCase; 
                                                         Color = colorCase } 
于 2012-12-27T09:52:18.097 に答える