.net 在F#中将Single Case discriminated union转换为int?

flseospp  于 2023-05-01  发布在  .NET
关注(0)|答案(3)|浏览(92)

我正在用F#编写一个国际象棋引擎,我正在考虑如何表示棋盘位置。
电路板数据结构是64个元素的数组。由于只有64个可能的索引,我想把它限制为一个单例区分的联合,而不是一个普通的int [<Struct>]type BoardPosition = Position of int,这将有助于我更好地推理,并可能减少测试的数量。
如何使用Position类型来索引数组?我试着测试将其转换为显式((int) data),但似乎不起作用
我的方法是否理想?使用区分联合是否有一些主要的开销,我应该坚持使用常规的int?

zsohkypk

zsohkypk1#

由于您已经将索引 Package 在一个新类型中,因此必须将该类型展开,以便访问原始索引。您可以使用模式匹配来实现这一点:

let get (Position idx) (board : _[]) =
    board[idx]

简单用法示例:

let board = [| "king"; "queen"; "pawn" |]
let pos = Position 1
get pos board
    |> printfn "%A"   // "queen"

这里有一些性能和认知开销,因此您必须决定是否值得。一般来说,这个主题被称为“primitive obsession”。

xxhby3vn

xxhby3vn2#

使用数组来表示棋盘绝对是一种选择--但是如果您想使用不可变的数据结构以函数的方式编写象棋引擎,那么它可能不会产生最好的代码。
我会考虑使用Map来代表董事会,将棋子分配到位置。这一点的好处是,它可以很容易地创建一个更改了一个位置的董事会副本(以便您可以使用它)。例如,模拟各种移动)。
我主要考虑的是写代码有多容易--效率是另一个关注点(但也许不是一个值得以后担心的问题)。我会从以下内容开始:

type Position = byte * byte

type Piece = 
  | Pawn
  | King
  | Queen

type Color = 
  | Black
  | White

type Board = Map<Position, Color * Piece>

let sampleMove (board:Board) = 
  if board.TryFind(3uy, 1uy) = Some(White, Pawn) then
    board.Remove((3uy, 1uy)).Add((3uy, 2uy), (White, Pawn))
  else board
js81xvg6

js81xvg63#

将元数据附加到int等原语的一种零开销方法是使用Units of Measure

type ArrayWithMeasure<'T, [<Measure>] 'M>(arr: 'T[]) =
    member _.Item(i: int<'M>) = 
        arr[int i]

[<Measure>]
type position

type Piece = 
| Pawn
| King
| Queen

let board = ArrayWithMeasure<Piece option, position>(Array.create 64 None)

let pos = 1<position>

board[pos]

上面展示了如何围绕数组创建度量安全 Package 器并访问它。

相关问题