F#中是否存在带键的记录类型
Does record type with key exist in F#
在 OPL(优化编程语言)中,我们有一个数据结构名称 tuple。 OPL元组对应F#中的Record。它是这样定义的:
tuple Point {
int x;
int y;
};
像在 F# 中一样,我们可以使用点表示法访问字段
int x = p.x;
我们可以将元组分组到一个集合中:
{Point} points = {<1,2>, <2,3>};
不同之处在于,就像在数据库系统中一样,元组结构可以与键相关联。元组键允许使用一组唯一标识符访问以元组组织的数据。在以下示例中,护士元组使用字符串类型的键名声明。
tuple nurse {
key string name;
int seniority;
int qualification;
int payRate;
}
{ nurse } nurses = …;
键的好处在于我们可以用这种方式初始化数组
int NumberOfChild [n in nurses] = 0;
并仅使用键访问值:
NumberOfChild[<"Isabelle">]=20;
省略没有键的字段。这相当于:
NumberOfChild[<"Isabelle",3,1,16>]=20;
此外,使用键意味着不会有两个具有相同键的元组。就像数据库中的主键。
问题是:F# 中是否存在这样的类型?用密钥记录?
我的目标: 我想定义一个有很多属性的节点结构。并通过仅提供节点的键而不是整个记录来加载图形结构,因为我将从数据库加载图形。
type Node = {
nodeKey : int;
nodeName : string;
nodeAttribute1 : string;
nodeAttribute2 : string }
let Graph = [
(1, 2);
(1, 3);
(2, 4);
(3, 4) ]
其中图元组中的整数表示nodeKey
.
我想使用图表进行操作,但仅使用密钥访问节点信息。
不,没有这样的语言级概念。可以说,所有记录字段都是平等创建的。
这并不妨碍您:
- 根据一个或多个字段值为记录合成一个键,
- 使用这样的键作为
Map
中的键来保存您的记录或任何其他值。
所以你可以有这样的东西:
type Nurse = { name: string; seniority: int; qualification: int; payRate: int }
let nurses = [ { name = "Isabelle"; seniority = 3; qualification = 1; payRate = 16 } ]
let numberOfChildren =
[ "Isabelle", 20 ]
|> Map.ofSeq
let nursesWithNumberOfChildren =
[ for nurse in nurses do
match numberOfChildren |> Map.tryFind nurse.name with
| Some children -> yield nurse, children
| None -> yield nurse, 0 ]
使用类似的方法,您可以分离图形和节点数据 - 仅在图形中存储键并维护从键到完整节点记录的映射。
//If I read data from a database, I would receive the data in the following form:
type XYZ = {X:int;
Y:string;
Z:float}
let recordsXYZ = [{X=1;Y="A";Z=1.0};{X=2;Y="b";Z=1.0};{X=3;Y="A";Z=1.0}]
//I can create a map this way
let mapXYZ1=recordsXYZ|>Seq.groupBy (fun a ->a.X)|>Map.ofSeq
//But I don't want a Map<int,seq<XYZ>>
//This is what I want
let mapXYZ2=recordsXYZ|>Seq.map (fun a -> (a.X,{X=a.X;Y=a.Y;Z=a.Z}))|>Map.ofSeq
//Or maybe this is cleaner but this need to define another type
type YZ = {Y:string;
Z:float}
let mapXYZ3=recordsXYZ|>Seq.map (fun a -> (a.X,{Y=a.Y;Z=a.Z}))|>Map.ofSeq
如果我没理解错的话,您最好的选择只是 Seq.groupBy
的一个更简洁的替代方案,以达到您的目的。这是它的核心,一行:
let inline project projection value = projection value, value
给定一个简单的辅助函数,不特定于 XYZ
let projectToMap projection values = values |> Seq.map (project projection) |> Map.ofSeq
从任何 "key":
干净地创建 XYZ
的地图变得微不足道
let mappedByX = xyzs |> projectToMap (fun { X=x } -> x) // Map<int, XYZ>
let mappedByY = xyzs |> projectToMap (fun { Y=y } -> y) // Map<string, XYZ>
let mappedByZY = xyzs |> projectToMap (fun { Y=y; Z=z } -> z, y) // Map<float*string, XYZ>
在 OPL(优化编程语言)中,我们有一个数据结构名称 tuple。 OPL元组对应F#中的Record。它是这样定义的:
tuple Point {
int x;
int y;
};
像在 F# 中一样,我们可以使用点表示法访问字段
int x = p.x;
我们可以将元组分组到一个集合中:
{Point} points = {<1,2>, <2,3>};
不同之处在于,就像在数据库系统中一样,元组结构可以与键相关联。元组键允许使用一组唯一标识符访问以元组组织的数据。在以下示例中,护士元组使用字符串类型的键名声明。
tuple nurse {
key string name;
int seniority;
int qualification;
int payRate;
}
{ nurse } nurses = …;
键的好处在于我们可以用这种方式初始化数组
int NumberOfChild [n in nurses] = 0;
并仅使用键访问值:
NumberOfChild[<"Isabelle">]=20;
省略没有键的字段。这相当于:
NumberOfChild[<"Isabelle",3,1,16>]=20;
此外,使用键意味着不会有两个具有相同键的元组。就像数据库中的主键。
问题是:F# 中是否存在这样的类型?用密钥记录?
我的目标: 我想定义一个有很多属性的节点结构。并通过仅提供节点的键而不是整个记录来加载图形结构,因为我将从数据库加载图形。
type Node = {
nodeKey : int;
nodeName : string;
nodeAttribute1 : string;
nodeAttribute2 : string }
let Graph = [
(1, 2);
(1, 3);
(2, 4);
(3, 4) ]
其中图元组中的整数表示nodeKey
.
我想使用图表进行操作,但仅使用密钥访问节点信息。
不,没有这样的语言级概念。可以说,所有记录字段都是平等创建的。
这并不妨碍您:
- 根据一个或多个字段值为记录合成一个键,
- 使用这样的键作为
Map
中的键来保存您的记录或任何其他值。
所以你可以有这样的东西:
type Nurse = { name: string; seniority: int; qualification: int; payRate: int }
let nurses = [ { name = "Isabelle"; seniority = 3; qualification = 1; payRate = 16 } ]
let numberOfChildren =
[ "Isabelle", 20 ]
|> Map.ofSeq
let nursesWithNumberOfChildren =
[ for nurse in nurses do
match numberOfChildren |> Map.tryFind nurse.name with
| Some children -> yield nurse, children
| None -> yield nurse, 0 ]
使用类似的方法,您可以分离图形和节点数据 - 仅在图形中存储键并维护从键到完整节点记录的映射。
//If I read data from a database, I would receive the data in the following form:
type XYZ = {X:int;
Y:string;
Z:float}
let recordsXYZ = [{X=1;Y="A";Z=1.0};{X=2;Y="b";Z=1.0};{X=3;Y="A";Z=1.0}]
//I can create a map this way
let mapXYZ1=recordsXYZ|>Seq.groupBy (fun a ->a.X)|>Map.ofSeq
//But I don't want a Map<int,seq<XYZ>>
//This is what I want
let mapXYZ2=recordsXYZ|>Seq.map (fun a -> (a.X,{X=a.X;Y=a.Y;Z=a.Z}))|>Map.ofSeq
//Or maybe this is cleaner but this need to define another type
type YZ = {Y:string;
Z:float}
let mapXYZ3=recordsXYZ|>Seq.map (fun a -> (a.X,{Y=a.Y;Z=a.Z}))|>Map.ofSeq
如果我没理解错的话,您最好的选择只是 Seq.groupBy
的一个更简洁的替代方案,以达到您的目的。这是它的核心,一行:
let inline project projection value = projection value, value
给定一个简单的辅助函数,不特定于 XYZ
let projectToMap projection values = values |> Seq.map (project projection) |> Map.ofSeq
从任何 "key":
干净地创建XYZ
的地图变得微不足道
let mappedByX = xyzs |> projectToMap (fun { X=x } -> x) // Map<int, XYZ>
let mappedByY = xyzs |> projectToMap (fun { Y=y } -> y) // Map<string, XYZ>
let mappedByZY = xyzs |> projectToMap (fun { Y=y; Z=z } -> z, y) // Map<float*string, XYZ>