映射区分联合中的所有值

Nic*_*ner 1 reflection f# discriminated-union

假设我有一个受歧视的联盟:

type card = Smithy | Cellar | Chapel
Run Code Online (Sandbox Code Playgroud)

并且将一个区分联合映射到某个值的函数:

let initialCardCount = function
  Smithy -> 4
  Cellar -> 6
  Chapel -> 2
Run Code Online (Sandbox Code Playgroud)

我想生成一个包含联盟中每个成员的成本的地图,例如:

[(Smithy, 4); (Cellar, 6); (Chapel, 2)]
Run Code Online (Sandbox Code Playgroud)

这可能吗?我希望能够做到这样的事情:

List.zip (allValues f) (List.map getCost (allValues f)) |> Map.ofList
Run Code Online (Sandbox Code Playgroud)

这个问题很有帮助,但这不是我需要的; 我希望能够访问内存中的union成员,而不仅仅是检查它们的属性.

我想要这样做的原因是我可以有一个区别性的联合代表游戏中所有不同类型的牌,以及一个功能,告诉你每张牌应该在开始时有多少,然后轻松生成初始将卡映射到计数.有一个更好的方法吗?

Tom*_*cek 5

您可以使用F#Reflection获取所有可能的区分联合案例的列表,但了解Reflection的缺点很重要 - 除非小心使用,否则很容易导致设计不佳(可能有更简单,更优雅的功能解决方案)和效率也较低(尽管您可能只需要获取所有案例的列表一次).

以下getAllValues函数获取所有情况 - 它假定所有情况都不存储任何值(即Smithy of string不起作用!)如果您想将一些参数传递给案例,则需要在第二个参数中传递盒装值MakeUnion:

open Microsoft.FSharp.Reflection

let getAllValues<'T>() : 'T list =
  let cases = FSharpType.GetUnionCases(typeof<'T>)
  [ for c in cases -> unbox (FSharpValue.MakeUnion(c, [| |])) ]
Run Code Online (Sandbox Code Playgroud)

以下是使用场景中的函数的示例:

type card = Smithy | Cellar | Chapel 

let initialCardCount = function 
  | Smithy -> 4 
  | Cellar -> 6 
  | Chapel -> 2 

let values = getAllValues<card>()
List.zip values (List.map initialCardCount values)
Run Code Online (Sandbox Code Playgroud)