0

我不确定标题中的“专有状态管理”内容,我尽我最大的努力试图简明扼要地提出问题。

我正在将我的一些 C# 代码移植到 F# 尝试尽可能地惯用它。我有一个实体从我的数据库中的序列中请求多个 ID,然后将这些 ID 分配给需要的任何人。一旦给出了一个 id,它就不应该再可供其他任何人使用。因此,必须有某种与该实体相关联的状态来跟踪剩余的 ID 数量。由于使用可变状态不是惯用的,我能做的就是写这样的东西:

let createIdManager = 
  let idToStartWith = 127
  let allowed = 10
  let givenOut = 0
  (idToStartWith, allowed, givenOut)
-
let getNextAvailableId (idToStartWith, allowed, givenOut) = 
  if givenOut< allowed
  then ((idToStartWith, allowed, givenOut+ 1), Some(idToStartWith + givenOut))
  else ((idToStartWith, allowed, givenOut), None)

let (idManager, idOpt) = getNextAvailableId createIdManager()
match idOpt with
| Some(id) -> printf "Yay!"
| None -> reloadIdManager idManager |> getNextAvailableId

这种方法是惯用的(据我所知)但非常脆弱。有很多方法可以弄乱它。我最大的担忧是,一旦一个 id 升级并制作了一个新的 id manager 副本,没有任何力量可以阻止您使用旧副本并再次获得相同的 id。

那么我如何在 F# 中进行排他状态管理?

4

2 回答 2

0

您可以使用状态单子(计算表达式)。

首先我们声明 state-monad

type State<'s,'a> = State of ('s -> 'a * 's)

type StateBuilder<'s>() =
  member x.Return v : State<'s,_> = State(fun s -> v,s)
  member x.Bind(State v, f) : State<'s,_> =
    State(fun s ->
      let (a,s) = v s
      let (State v') = f a
      v' s)

let withState<'s> = StateBuilder<'s>()
let runState (State f) init = f init

然后我们定义你的'IdManager'和一个函数来获取下一个可用的id以及函数执行后的新状态。

type IdManager = {
  IdToStartWith : int
  Allowed : int
  GivenOut : int
}

let getNextId state = 
  if state.Allowed > state.GivenOut then
    Some (state.IdToStartWith + state.GivenOut), { state with GivenOut = state.GivenOut + 1 }
  else
    None, state

最后,我们定义了请求 id 并执行 state-monad 的逻辑。

let idStateProcess =
  withState {
    let! id1 = State(getNextId)
    printfn "Got id %A" id1

    let! id2 = State(getNextId)
    printfn "Got id %A" id2

    //...

    return ()
  }

let initState = { IdToStartWith = 127; Allowed = 10; GivenOut = 0  }

let (_, postState) = 
  runState 
    idStateProcess 
    initState //This should be loaded from database in your case

输出:

Got id Some 127
Got id Some 128
于 2014-02-01T19:37:42.653 回答
0

如果您只需要初始化一次 id 集,那么您可以简单地隐藏对本地函数范围内列表的可变引用,如下所示:

let nextId =
    let idsRef = ref <| loadIdsFromDatabase()
    fun () ->
        match idsRef.Value with
        | []        -> 
            None
        | id::ids   ->
            idsRef := ids
            Some id

let id1 = nextId ()
let id2 = nextId ()
于 2013-09-29T17:03:27.733 回答