Как сохранить метод, а затем передать ему несколько параметров - PullRequest
1 голос
/ 25 апреля 2019

Я пытаюсь отследить использование значений, поэтому я обертываю метод, генерирующий указанное значение и входные данные (которые также переносятся) в класс, который я называю 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 Ответ

5 голосов
/ 26 апреля 2019

Я начал отвечать на вопрос, но в итоге я сделал пару изменений в структуре вашего примера, так что это уже не прямой ответ, а скорее другой подход к решению проблемы, который, я полагаю, вы пытаетесьрешать.Надеюсь, это все равно поможет!

Вместо того, чтобы использовать конкретный класс для 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 создает слот данных с начальным значением и возвращает установщик вместе со слотом данных.Вы можете использовать функцию установки для изменения значения в слоте данных.
  • Оператор <*> берет слот данных, содержащий функцию, слот данных, содержащий аргумент, и возвращает слот данных с результатом - результатамиизменяется каждый раз, когда изменяется функция или аргумент.

Стоит отметить, что оператор <*> и функция immutableSlot являются шаблоном, который Haskellers вызывает аппликативный функтор .Приятно то, что благодаря тому, как работает частичное приложение и каррирование, теперь вы также можете использовать функции с несколькими аргументами:

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 () = () } }

Оператор <*> более уродлив, поскольку ему необходимо подписаться на уведомления по двум аргументам.Однако, чтобы избежать утечек памяти, необходимо также отписаться, когда число зарегистрированных подписок достигнет нуля (я действительно написал статью об этой утечке памяти !)

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.Мы могли бы просто подписаться и никогда не отменять подписку (и обновлять всегда с нетерпением), но это может привести к утечкам памяти, если вы неоднократно подписываетесь и отписываетесь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...