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

Sve*_*inz 1 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
Run Code Online (Sandbox Code Playgroud)

Tom*_*cek 5

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

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

type Dataslot<'T> = 
  abstract Value : 'T
  abstract Subscribe : (unit -> unit) -> IDisposable
Run Code Online (Sandbox Code Playgroud)

我的订阅机制与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>
Run Code Online (Sandbox Code Playgroud)
  • immutableSlot 创建一个永不更改并始终返回初始值的数据插槽。
  • mutableSlot创建一个具有初始值的数据插槽,然后将设置器与数据插槽一起返回。您可以使用设置器功能更改数据插槽中的值。
  • <*>运算符采用包含含有自变量的函数,数据时隙中的数据时隙,并返回结果的数据时隙-的结果改变每当函数或自变量的变化。

值得注意的是,<*>运算符和immutableSlot函数是Haskellers称为应用函子的模式。令人高兴的是,由于部分应用程序和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 )
Run Code Online (Sandbox Code Playgroud)

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

setB 32
setB 30    
sub.Dispose()   
setB 1
Run Code Online (Sandbox Code Playgroud)

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

mutableSlot 每当调用setter时都需要触发change事件:

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

immutableSlot 更容易,因为它永远不会改变:

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

<*>运营商是难看,因为它需要订阅它的两个参数的通知。但是,为避免内存泄漏,当向其注册的订阅数达到零时,它也需要取消订阅(我实际上写了一篇有关此内存泄漏的论文!)

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

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