parameters - 如何存储方法并稍后为其提供多个参数
问题描述
我正在尝试跟踪值的使用情况,因此我将生成所述值的方法和输入(也被包装)包装到一个名为 Dataslot 的类中。我不知道我将预先包装什么方法和什么值,所以我尝试了各种方法来编写它,并认为我下面的代码可以工作。但let mutable value = funk unpack
似乎并没有导致 funk beeing 被识别为一个函数,所以方法 unpack 似乎是错误的方法,我该如何让它工作?
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
解决方案
我开始回答这个问题,但最后我对你的例子的结构做了一些改变,所以它不再是一个直接的答案——而是另一种解决我想你正在尝试解决的问题的方法。希望这仍然会有所帮助!
我没有使用具体的类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 函数来更改数据槽中的值。- 运算符获取包含函数的
<*>
数据槽,包含参数的数据槽并返回带有结果的数据槽 - 只要函数或参数更改,结果就会更改。
值得注意的是<*>
operatorn 和immutableSlot
function 是 Haskellers 称之为applicative functor的一种模式。好消息是,由于部分应用和柯里化的工作原理,您现在也可以使用多参数函数:
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
每当调用 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 } }
immutableSlot
更容易,因为它永远不会改变:
let immutableSlot value =
{ new Dataslot<'T> with
member x.Value = value
member x.Subscribe _ =
{ new IDisposable with member x.Dispose () = () } }
<*>
操作符更丑,因为它需要订阅关于它的两个参数的通知。但是,为了避免内存泄漏,它还需要在注册的订阅数达到零时取消订阅(我实际上写了一篇关于此内存泄漏的论文!)
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
访问时懒惰地重新计算。我们可以只订阅并且从不取消订阅(并且总是急切地更新),但是如果您反复订阅和取消订阅,这可能会导致内存泄漏。
推荐阅读
- cloud - OpenTracing+Jaeger 语言无关
- mysql - 如何将复杂的 JSON 批量上传到 MySQL
- c# - 用于翻译的资源突然没有被翻译
- reactjs - 导航栏组件未使用 react 和 react-bootstrap 加载
- session - 无法创建 kie 会话
- maven - 如何将我的 Spring Boot 的静态资源与构建分开
- python - 如何找到数字数组拐点的估计值?(Python)
- sql - SQL 格式('MMMM')在 SQL Server 2008 中不起作用
- git - 从 PowerShell 脚本中提取时出错
- java - @RunWith(CucumberWithSerenity.class) 抛出 NoClassDefFound cucumber/runtime/junit/Assertions