2016-07-04 112 views
5

我试图做一个集合,持有多种可能的类型。这是如何我希望它看起来一个例子:如何使一个特定的类型属于一个类型的家族?

type Position = Vector2 
type Velocity = Vector2 
type Appearance = String 

type Component = Position | Velocity | Appearance 

let components = List<Dictionary<string, Component>>() 
let pos = Dictionary<string, Position>() 

components.Add(pos) // Type "Position" does not match with type "Component" 

我要声明特定类型仍然适合在一般的类型。有没有一种方法可以像这样编写我的代码?有没有更习惯的方式来做到这一点?

+3

你知道Component.Position和Position不是同一个吗? –

回答

6

有一对夫妇的事情,你在你的代码在这里有:

类型缩写Link

type Position = Vector2 
type Velocity = Vector2 
type Appearance = String 

类型的缩写只是定义另一个名称为现有的类型,该类型左右和精确等价,可以互换使用。

为了给出与此问题相关的示例,在F#中有标准.NET List的类型缩写,它被称为ResizeArray。它的定义是这样的:

type ResizeArray<'T> = System.Collections.Generic.List<'T> 

它可以节省您不必为了使用它来打开System.Collections.Generic和它有助于避免与F#中list类型混乱,但它并没有做其他比添加一个新的名称,也现有类型。

Disciminated工会Link

type Component = Position | Velocity | Appearance 

在这里,你有一个单一的类型,称为Component,你可以把它作为一个单一类型三个构造函数:PositionVelocityAppearance。您还可以通过模式匹配使用相同的三种情况再次解构类型。

例如

match comp with 
|Position -> .. 
|Velocity -> .. 
|Appearance -> .. 

希望,它现在应该是毫不奇怪,你声明的类型缩写Position与工会的情况下Position你宣布为Component类型的一部分没有任何联系。他们完全相互独立。

Position意味着Vector2Component是一个完全独立的联合类型。

假设您想要一个可包含多个事物的Component类型,您需要将某些值与案例相关联。下面是建立这样一个区分联合的例子:

type Component = 
    | Position of Vector2 
    | Velocity of Vector2 
    | Appearance of string 

现在,让我们来看看下一个问题。

如果我们删除该类型的缩写和尝试的代码的其余部分与我们的新识别联合

let components = List<Dictionary<string, Component>>() 
let pos = Dictionary<string, Position>() 

我们现在有一个新的错误:

The type Position is not defined.

好,记住我刚才所说约ComponentComponent是类型,Position不是一种类型,它是一个Component的结合案例。

如果你想,以遏制这些选项同样一个字典全部,你可能会更好改变你的定义是这样的:

type ComponentDictionary = 
    |PositionDictionary of Dictionary<string, Vector2> 
    |VelocityDictionary of Dictionary<string, Vector2> 
    |AppearanceDictionary of Dictionary<string, string> 

然后,你可以创建它们的ResizeArray/List。现在

let components = ResizeArray<ComponentDictionary>() 

,为了填充这个集合,然后我们只需要使用适当的情况下,构造函数ComponentDictionary

let pos = PositionDictionary (Dictionary<string, Vector2>()) 

现在,pos是ComponentDictionary型的,所以我们可以把它添加到组件:

components.Add(pos) // No error here! 
1

TheInnerLight的答案非常完整。我想补充一点,使用纯F#类型(list,map或dict)而不是泛型.Net List和Dictionary可能会有一些优势。当然,你可能实际上需要.NET通用集合。下面是使用地图(或字典)一个简单的例子,并创建各种组件的地图列表:

type Vector2 = float * float 

type Component = 
    | Position of Vector2 
    | Velocity of Vector2 
    | Appearance of string 

type Components = list<Map<string,Component>> // this is not strictly necessary 

let pos = Position(10.,20.) 
let app = Appearance "here" 

let compMap1= Map.empty<string,Component> // define an empty map for Component 
let compMap1 = compMap1.Add("key1",pos).Add("key2",app) // or: let compMap1' = dict(["key1",pos;"key2",app]) 
let compMap2 = ["key3",pos;"key4",pos] |> Map.ofList // you can create map from a list 
let (components:Components) = [compMap1;compMap2] // make a list of your maps 
let components' = [compMap1;compMap2] // or just use a generic F# list 
(* 
val components' : Map<string,Component> list = 
    [map [("key1", Position (10.0, 20.0)); ("key2", Appearance "here")]; 
    map [("key3", Position (10.0, 20.0)); ("key4", Position (10.0, 20.0))]] 
*) 

如果将只需要一个列表(或阵列,或其他)持有不同组件类型,你已经可以做到这一点:

let compList = [pos;app] 
//val compList : Component list = [Position (10.0, 20.0); Appearance "here"] 
相关问题