我更喜欢使用F#中的类来记录,因为我不需要实现相等和哈希码。但是,它们似乎没有提供确保记录值不变量的工具。
例如,假设我正在为数独板建模。我可能会写一个这样的记录类型:
type Sudoku = { Values : Map<(int * int), int> }
let empty = { Values = Map.empty }
现在的问题是我对Values
地图有一些要求:
(x, y)
的{{1}}和x
必须介于0和8之间(包括0和8)使用类型,我可以在构造函数中检查这些,如果不满足则引发异常。这样,使用y
的任何代码都由构造函数保证满足不变量。
如何用记录实现这一目标?
答案 0 :(得分:6)
我会通过为键和值创建单例联合类型来实现这一点,为这些类型提供私有构造函数:
[<Struct>] type SudokoKey = private SudokoKey of int
[<Struct>] type SudokoValue = private SudokoValue of int
然后,我将为单元格和板创建记录类型,再次给出类型私有构造函数:
type SudokoCell =
private {
X: SudokoKey
Y: SudokoKey
}
type SudokoBoard =
private {
Cells: Map<SudokoCell, SudokoValue>
}
在定义了数据类型的情况下,我会根据您所概述的不同失败案例创建歧视联盟:
type SudokoError =
| KeyOutOfRange of int
| ValueOutOfRange of int
| ValuesNotUniqueAcrossRow of Map<(int * int), int>
| ValuesNotUniqueAcrossColumn of Map<(int * int), int>
| ValuesNotUniqueIn3x3Cell of Map<(int * int), int>
接下来,我将创建用于创建密钥的模块,执行验证的值将返回带有键/值或相应错误的Result
。这些模块将使用私有构造函数来实例化单例联合类型:
module SudokoKey =
let create key =
if key < 0 || key > 8
then Error <| KeyOutOfRange key
else Ok <| SudokoKey key
let value (SudokoKey key) = key
module SudokoValue =
let create value =
if value < 1 || value > 9
then Error <| ValueOutOfRange value
else Ok <| SudokoValue value
let value (SudokoValue value) = value
然后我为SudokoCell
创建一个类似的模块,该模块使用SudokoKey.create
和x
整数上应用的y
函数,以及单元格的私有构造函数输入,用单元格或相应的错误创建Result
:
module SudokoCell =
let create (x,y) =
x |> SudokoKey.create
|> Result.bind (fun xKey ->
y |> SudokoKey.create
|> Result.bind (fun yKey ->
Ok { X = xKey; Y = yKey }))
最后,您只需要一个SudokoBoard
模块来创建电路板本身,并在地图上应用剩余的验证规则。我已经将此删除了,但将实施作为练习:
module SudokoBoard =
// Map<(int * int), int> -> SudokoBoard
let create cells =
// Implementation of validations for board left as exercise
有了这个,你就有了Sudoko董事会的描述模型,它可以验证所有值,防止创建非法密钥,值,单元格或电路板,如果使用了无效值,则始终返回信息性错误
答案 1 :(得分:4)
https://stackoverflow.com/a/13925632/2314532可能有一个答案:你可以写下如下内容:
type Sudoku = private { _values : Map<(int * int), int> } with
member public this.Values = this._values
static member public Create (values : Map<(int * int), int>) =
// Do your constraint checking here
{ _values = values }
请注意,我自己没有对此进行过测试,因此如果您发现这不起作用,请发表您所学内容的评论。