我会通过为键和值创建单例联合类型来做到这一点,并为这些类型提供私有构造函数:
[<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 板的描述性模型,它可以验证所有值,防止创建非法键、值、单元格或板,如果使用无效值,则始终返回信息性错误.