Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Functional programming 函数式编程:如何建立关系模型?_Functional Programming_F#_Modeling - Fatal编程技术网

Functional programming 函数式编程:如何建立关系模型?

Functional programming 函数式编程:如何建立关系模型?,functional-programming,f#,modeling,Functional Programming,F#,Modeling,我想知道如何在函数式编程中建立关系模型 以员工为例: 员工可以与0..n名同事建立友谊。 友谊总是相互的:如果A是B的朋友,那么B就是A的朋友 我如何建模?我有3个想法(如下所列)。它们都有缺点 第一次尝试: type Employee = { name : string friendships : Employee list } // My list is mutable because I want to be able to update employees. // I

我想知道如何在函数式编程中建立关系模型

以员工为例: 员工可以与0..n名同事建立友谊。 友谊总是相互的:如果A是B的朋友,那么B就是A的朋友

我如何建模?我有3个想法(如下所列)。它们都有缺点

第一次尝试:

type Employee =
  { name : string
    friendships : Employee list
  }

// My list is mutable because I want to be able to update employees.
// I suppose the same problems would occur with e. g. Elmish.
let mutable employees : Employee list = [ (* ... *) ]

(* Downsides:
   Friendships are copies. Changes to an employee would have to be propagated manually.
*)
第二次尝试:

type EmployeeId = int

type Employee =
  { id : EmployeeId
    name : string
    friendships : EmployeeId list
  }

let mutable employees : Employee list = [ (* ... *) ]

(* Downsides:
   Friendships are not modeled as mutual.
   When the friendship of an employee changes,
   the friend's friendships don't change accordingly.
*)
第三次尝试:

type EmployeeId = int

type Friendships = list<EmployeeId * EmployeeId>

type Employee =
  { id : EmployeeId
    name : string
  }

let mutable employees : Employee list = [ (* ... *) ]

(* Downsides:
   After deleting an employee from the list,
   employeeFriendships contain invalid "references"
   to the deleted Employee.
*)
类型EmployeeId=int
键入友谊=列表
类型员工=
{id:EmployeeId
名称:string
}
让可变员工:员工列表=[(*…*)]
(*缺点:
从列表中删除员工后,
员工友谊包含无效的“推荐信”
发送给已删除的员工。
*)

这能做得更好吗?谢谢。

使用
let rec
/
可能是最直接的方法。你需要通过将
友谊
更改为
IEnumerable
来引入懒惰

类型员工=
{ 
名称:string
友情:seq
}
让rec peter={name=“peter”;friendships=seq{yield paul;yield mary}
保罗={name=“paul”;友谊=seq{yield peter;yield mary}
而玛丽{name=“mary”;友谊{yield peter;yield paul}}
但是,编译器会发出以下警告:

此引用和其他对所定义对象的递归引用 将在运行时通过 延迟引用的使用。这是因为您正在定义一个或多个 更多的递归对象,而不是递归函数。这一警告 可以通过使用“#nowarn”40”或“--nowarn:40”来抑制

…这表明了一个可以说更好的解决方案:

类型员工=
{ 
名称:string
友谊:单位->员工名单
}
让rec peter={name=“peter”;friendships=fun()->[paul;mary]}
保罗={name=“paul”;friendships=fun()->[peter;mary]}
玛丽={name=“mary”;friendships=fun()->[peter;paul]}

问题不在于函数式编程,因为它涉及到变异。面向对象的解决方案可以很好地工作,因为重要的是将逻辑封装在一个地方,并将其隐藏在一个安全的API后面。幸运的是,您对F#的选择仍然很好,因为F#是一种非常好的OO语言

type Employee(id:int,name:string)=
静态let employees=ResizeArray[]()
静态let friendships=ResizeArray()
静态let getEmployee(eid:EmployeeId)=雇员|>Seq.find(乐趣e->e.Id=eid)
成员t.Id=Id
成员t.名称=名称
成员t.朋友=
让FriendID=友谊。。。
friendIds |>Array.map getEmployee
静态成员Employees=Employees |>Array.ofSeq
静态成员Add=employees.Add
静态成员删除(e:员工)=
员工。删除e |>忽略
友谊|>顺序过滤(乐趣(a,b)->a=e.Id | | b=e.Id)
|>Seq.iter(friendships.Remove>>忽略)
静态成员AddFriend(e1:Employee,e2:Employee)=。。。

您也可以对记录和/或模块执行相同的操作,方法是将内容设置为“私有”
private

这类问题的最佳答案在很大程度上取决于您希望如何使用这些数据。根据用例的不同,有不同的“正确方法”来实现。例如,如果您想显示社交圈的交互式地图,那么拥有一个具体化的树,其中包含每个友谊的实际员工数据可能很有价值。但是,如果您只想查询一个人是否是另一个人的朋友,那么在
Employee
对象上只需要一个朋友ID列表就足够了。我只是想知道如何建模。我试图在网上找到解释,结果却很少。阅读建议不胜感激。什么是“更好”?2.如果你依赖突变,它就不起作用。3.这个问题实际上与函数式编程没有任何关系。你实际上想要的是如何保持一个不变量,答案是,不管什么样的范式,都倾向于封装。如何实现这一点对于堆栈溢出来说是一个很好的问题,但事实并非如此。它太宽泛了,需要征求意见。你会希望有一个模型,在保持实用性的同时,减少不一致的可能性。一种可能性是考虑您希望如何在外部存储/检索数据,因为相同的范例可以在内部使用。在这种特殊情况下,单独列出员工和关系对我来说是有意义的(选项3)。可以使用
集合
,并确保始终存储元组以避免重复/歧义。在这种情况下,您指出的缺点是不可避免的,但并不严重。选项1会产生大量冗余,维护非常困难,并且产生不一致的可能性非常高。选项2也可能在一名员工所说的与另一名员工所说的不一致(尽管这会使它更真实!)。更好的解决方案的问题是它是硬编码的,但您可以轻松创建一个具有不同
友谊的副本
{peter with friendships=fun()->[]}