我正在尝试将域建模为记录和可区分的联合,即不可变的。我注意到,我有一个 m:n 关系,比如作者和书籍。现在我正在寻找一种表达它的好方法,例如:
- 最好是不可变的
- 更改一本书的属性很容易,也就是说,如果我想创建一个副本,我不必遍历所有作者
- 访问简单;理想情况下,作者有一个书单
到目前为止,我没有找到满足所有标准的解决方案。我发现的是:
- 每个作者都有一个书单(我真的不需要相反的方向)。这允许轻松访问并且是不可变的,但是更改一本书的属性需要更改每个作者
- 拥有一个不可变的书籍引用单元列表,每个作者都有一个不可变的引用单元列表。这使得更改属性和访问变得容易,但这实际上并不是不可变的,因为引用单元格不是不可变的。
- 拥有一个不可变的图书列表,每个作者都有一个不可变的 ID 列表。这是不可变的,可以轻松更改书籍,但访问起来比较困难,因为您需要进行查找并且您可能拥有不存在的 ID。
是否有任何解决方案可以满足上述所有条件,比如不可变的引用单元?如果是这样,它们看起来像什么。
最佳答案
正如我在评论中提到的,如果您有一个不可变的作者列表并对其进行映射,您实际上并不会最终复制所有作者。不改变的作者只会指向同一个实例(所以不可变解决方案的开销不是那么大)。
但是,如果您有多本同一作者的书,则不会出现别名,因此作者都是不同的值(并且您必须映射所有书)。
我认为在这种情况下合理的表示是将作者和书籍分开并通过键(例如作者姓名 - 在下面的示例中 - 或其他一些 ID)将它们链接起来:
type Author = { Name : string; Address : string }
type Book = { Title : string; Author : string }
// For efficient lookup, create a hashtable with authors
let authors = dict [ "Tomas", { Name = "Tomas"; Address = "Cambridge" } ]
// Books are stored simply as a list
let books = [ { Title = "Real World FP"; Author = "Tomas" } ]
// To get nice access, add AuthorDetails property to the Author record
type Book with
member x.AuthorDetails = authors.[x.Author]
for book in books do
printfn "%s (%s, %s)" book.Title book.Author book.AuthorDetails.Address
这不会让您改变集合 authors
。如果您以纯函数式方式编写此代码,您可能会有一些递归函数将当前作者作为参数,因此您不需要修改(只需构建一个新字典)。
但我认为用一个 ref
值来保存字典是合理的,或者甚至保留一个可变字典(但我只会在没有并发的情况下这样做;事实上,存在的并发性,Map
可能是更安全的选择)。
关于reference - 如何在 F# 中表达关系,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18318150/