如何存储方法并在以后为它提供多个参数

时间:2019-04-25 17:34:23

标签: parameters f#

我试图跟踪值的使用,因此将生成所述值和输入(也被包装)的方法包装到一个称为Dataslot的类中。 我不知道我会预先包装什么方法和什么值,所以我尝试了多种方式编写此代码,并认为下面的代码可以工作。 但是let mutable value = funk unpack似乎并没有导致将funk beeing识别为一个函数,因此解压缩方法似乎是错误的方法,我该如何使用它?

type Dataslot(funk, input:Dataslot[]) as self =

    let mutable reffunk= funk
    let refinput=input
    let unpack= for inpu in refinput do inpu.Value
    let mutable value = funk unpack
    let uses= ResizeArray<Dataslot>[]
    let get1()=
       value
    let mutable get0=fun()->get1()
    let get2()=
       value<-reffunk unpack
       get0<-fun()->get1()
       value
    do for inpu in refinput do inpu.Subscribe(self)
    member x.setfunk(fu)=
        reffunk<-fu
        for u in uses do
            u.Changed
    member x.setinput(index:int, inp:Dataslot)=
        refinput.[index].Unsubscribe(self)
        refinput.[index]=inp
        refinput.[index].Subscribe(self)
        for u in uses do
            u.Changed
    member x.Value
        with get()=get0()
    member x.Changed=get0<-fun()->get2()
    member x.Subscribe(f)=
        uses.Add(f) |>ignore
    member x.Unsubscribe(f)=
        uses.Remove(f) |>ignore

1 个答案:

答案 0 :(得分:5)

我开始回答这个问题,但是我最终对您的示例的结构进行了一些更改,因此它不再是直接的答案-而是解决我认为您正在尝试解决的问题的另一种方法解决。希望这仍然会有所帮助!

我没有使用Dataslot的具体类,而是使用了一个接口,并且使该接口通用,因此Dataslot<'T>表示类型为'T的值:

type Dataslot<'T> = 
  abstract Value : 'T
  abstract Subscribe : (unit -> unit) -> IDisposable

我的订阅机制与IObservable的工作原理更相似-您为它提供了一个函数,该函数应在值更改时调用,并且该函数返回一个IDisposable,可用于取消订阅和停止收到有关更改的通知。

然后,我定义了以下三个可用于处理数据插槽的基元(以下实现):

val mutableSlot   : initial:'T -> ('T -> unit) * Dataslot<'T>
val immutableSlot : value:'T -> Dataslot<'T>
val ( <*> )       : f:Dataslot<('T -> 'R)> -> a:Dataslot<'T> -> Dataslot<'R>
  • immutableSlot创建一个永不更改且始终返回初始值的数据槽。
  • mutableSlot创建一个具有初始值的数据插槽,并返回一个setter和数据插槽。您可以使用设置器功能更改数据插槽中的值。
  • <*>运算符获取一个包含函数的数据插槽,包含一个参数的数据插槽,并返回带有结果的数据插槽-每当函数或参数更改时,结果都会更改。

值得注意的是,<*>运算符和immutableSlot函数是Haskellers称为applicative functor的模式。令人高兴的是,由于部分应用程序和currying的工作原理,您现在还可以使用多参数函数:

let a = immutableSlot 10
let setB, b = mutableSlot 30
let res = immutableSlot (fun a b -> a + b) <*> a <*> b

let sub = res.Subscribe(fun () -> 
  printfn "Result changed to: %d" res.Value )

现在,您可以尝试触发几次更改,然后调用Dispose取消订阅通知:

setB 32
setB 30    
sub.Dispose()   
setB 1

这三个操作的实现与您最初编写的某些代码非常相似。造成此丑陋的主要原因是跟踪发生更改时需要通知的处理程序。

mutableSlot每当调用设置器时都需要触发更改事件:

let mutableSlot initial =
  let mutable value = initial
  let handlers = ResizeArray<_>()
  (fun newValue ->
    value <- newValue
    for h in handlers do h()),
  { new Dataslot<'T> with
    member x.Value = value
    member x.Subscribe h = 
      handlers.Add(h)
      { new IDisposable with 
        member x.Dispose() = handlers.Remove(h) |> ignore } }

immutableSlot更容易,因为它从不改变:

let immutableSlot value = 
  { new Dataslot<'T> with
    member x.Value = value
    member x.Subscribe _ = 
      { new IDisposable with member x.Dispose () = () } }

<*>运算符比较丑陋,因为它需要订阅有关其两个参数的通知。但是,为避免内存泄漏,当向其注册的订阅数达到零(我实际上是wrote a paper about this memory leak!)时,还需要取消订阅

let (<*>) (f:Dataslot<'T -> 'R>) (a:Dataslot<'T>) =
  let mutable value = f.Value a.Value
  let handlers = ResizeArray<_>()
  let update () = 
    value <- f.Value a.Value
    for h in handlers do h()
  let mutable fsub = { new IDisposable with member x.Dispose() = () }
  let mutable asub = { new IDisposable with member x.Dispose() = () }
  { new Dataslot<'R> with
    member x.Value = 
      if handlers.Count > 0 then value else f.Value a.Value
    member x.Subscribe h = 
      handlers.Add(h)
      if handlers.Count = 1 then 
        fsub <- f.Subscribe(update)
        asub <- a.Subscribe(update)
        value <- f.Value a.Value
      { new IDisposable with 
        member x.Dispose() = 
          handlers.Remove(h) |> ignore 
          if handlers.Count = 0 then
            fsub.Dispose()
            asub.Dispose() } }

编辑<*>的实现中有一个非常棘手的方面,那就是它何时重新计算其值。如果有人订阅了更改通知,我们假设他们将需要该值,因此每次(其中一个)参数发生更改时,我们都会重新计算该值。当没有人订阅时,我们假设他们可能无法访问该值,因此我们仅在访问Value时才进行延迟计算。我们可以只订阅而永不退订(并且总是热切地更新),但是如果您反复订阅和退订,则可能导致内存泄漏。