对F#记录的限制?

sdg*_*sdh 2 oop f# record

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

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

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

let empty = { Values = Map.empty }
Run Code Online (Sandbox Code Playgroud)

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

  • 每个键(x, y)必须xy介于0和8,包容性
  • 每个值必须介于1和9之间
  • 每一个值在一行中必须是唯一的
  • 每列的值必须是唯一的
  • 每个值在3x3单元格内必须是唯一的

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

我如何通过记录实现这一目标?

Aar*_*ach 6

我会通过为键和值创建单例联合类型来实现这一点,为这些类型提供私有构造函数:

[<Struct>] type SudokoKey = private SudokoKey of int
[<Struct>] type SudokoValue = private SudokoValue of int
Run Code Online (Sandbox Code Playgroud)

然后,我将为单元格和板创建记录类型,再次给出类型私有构造函数:

type SudokoCell =
    private {
        X: SudokoKey
        Y: SudokoKey
    }

type SudokoBoard =
    private {
        Cells: Map<SudokoCell, SudokoValue>
    }
Run Code Online (Sandbox Code Playgroud)

在定义了数据类型的情况下,我将根据您概述的不同故障情况创建区别性联合:

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>
Run Code Online (Sandbox Code Playgroud)

接下来,我将创建用于创建密钥的模块,执行验证的值将返回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
Run Code Online (Sandbox Code Playgroud)

然后我创建一个类似的模块SudokoCell,使用SudokoKey.create应用于整数xy整数的函数,以及单元格类型的私有构造函数,创建一个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 }))
Run Code Online (Sandbox Code Playgroud)

最后,您只需要一个SudokoBoard模块来创建电路板本身,在地图上应用剩余的验证规则.我已将其删除,但将实现作为练习:

module SudokoBoard =
    // Map<(int * int), int> -> SudokoBoard
    let create cells =
        // Implementation of validations for board left as exercise
Run Code Online (Sandbox Code Playgroud)

有了这个,您就可以使用Sudoko板的描述模型来验证所有值,防止创建非法密钥,值,单元格或电路板,如果使用了无效值,则始终返回信息性错误.