我试图制作一个包含多种可能类型的集合。这是我希望它看起来的一个例子:
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"
我想声明仍然适合一般类型的特定类型。有没有办法可以像这样写我的代码?有没有更惯用的方法呢?
答案 0 :(得分: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
类型混淆,但除了为其添加新名称之外,它不会做任何其他事情。现有的类型。
受歧视的工会(Link)
type Component = Position | Velocity | Appearance
这里有一个名为Component
的单一类型,您可以将其视为具有三个构造函数的单一类型:Position
,Velocity
和Appearance
。您还可以通过模式匹配使用相同的三种情况再次解构类型。
e.g。
match comp with
|Position -> ..
|Velocity -> ..
|Appearance -> ..
希望您声明的类型缩写Position
与您声明为Position
类型的一部分的联合案例Component
没有任何联系,这应该不足为奇了。它们完全相互独立。
Position
表示Vector2
,Component
是完全独立的联合类型。
假设您想要一个可能包含多个内容的Component
类型,则需要将一些值与案例相关联。以下是创建此类歧视联盟的示例:
type Component =
| Position of Vector2
| Velocity of Vector2
| Appearance of string
现在,让我们看看下一个问题。
如果我们删除类型缩写并使用我们新的Discriminated Union
尝试其余代码let components = List<Dictionary<string, Component>>()
let pos = Dictionary<string, Position>()
我们现在有一个新错误:
未定义类型
Position
。
好吧,请记住我之前所说的关于Component
的内容。 Component
是类型,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
使用适当的case构造函数
let pos = PositionDictionary (Dictionary<string, Vector2>())
现在,pos的类型为ComponentDictionary
,因此我们可以将其添加到组件中:
components.Add(pos) // No error here!
答案 1 :(得分:0)
TheInnerLight的答案非常完整。我想补充一点,使用纯F#类型(列表,地图或字典)可能有一些优势,而不是通用的.Net列表和字典。当然,您可能确实需要.NET泛型集合。这是一个使用map(或dict)并创建各种Component map列表的简单示例:
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"]