对F#记录的限制?

时间:2018-06-14 12:23:25

标签: oop f# record

我更喜欢使用F#中的类来记录,因为我不需要实现相等和哈希码。但是,它们似乎没有提供确保记录值不变量的工具。

例如,假设我正在为数独板建模。我可能会写一个这样的记录类型:

type Sudoku = { Values : Map<(int * int), int> }

let empty = { Values = Map.empty }

现在的问题是我对Values地图有一些要求:

  • 每个密钥(x, y)的{​​{1}}和x必须介于0和8之间(包括0和8)
  • 每个值必须介于1到9之间,包括
  • 每一个值在一行中必须是唯一的
  • 每个值在列中必须是唯一的
  • 每个值在3x3单元格内必须是唯一的

使用类型,我可以在构造函数中检查这些,如果不满足则引发异常。这样,使用y的任何代码都由构造函数保证满足不变量。

如何用记录实现这一目标?

2 个答案:

答案 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.createx整数上应用的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 }

请注意,我自己没有对此进行过测试,因此如果您发现这不起作用,请发表您所学内容的评论。