Как накапливать наблюдаемые - PullRequest
0 голосов
/ 21 декабря 2018

Я должен определить функцию, которая будет возвращать IObservable <'u>

accumulate : ('t -> 'a -> 't * 'u option) -> 't -> IObservable<'a> -> IObservable<'u>

, чтобы моя функция ft obs' накапливала наблюдаемые события obs в накопитель типа 't и испускала наблюдаемое событиеu, когда 'snd (f acc a)' оценивается как 'Some u' для наблюдаемого события 'a'.

До сих пор я реализовал следующую функцию:

let accumulate (f:'t -> 'a -> 't * 'u option) t obs = 
 Observable.scan (fun _ x -> snd (f t x)) None obs

Я действительноя не понимаю, как работает эта наблюдаемая функция сканирования, в этом случае моя функция возвращает IObservable <'u option>.Как я могу это исправить?Я на правильном пути?

Ответы [ 2 ]

0 голосов
/ 21 декабря 2018

Функция fun _ x -> snd (f t x) не завершена.Подсказка в том, что первый параметр _ игнорируется, а первая часть результирующего кортежа выбрасывается при вызове snd.

Нет накопления, потому что f t x всегда вызывает с тем же значением t, которое было первоначально передано accumulate.Предполагается, что этот исходный t является начальным значением и должен быть передан в scan как часть его второго параметра.

Первая часть кортежа, созданного с помощью f:'t -> 'a -> 't * 'u option, является накопленным значением.Так что это та часть, которую нужно вернуть в scan, чтобы она снова передавалась в f и накапливалась снова и снова.

В вашей задаче необходимо накапливать, а также передавать событие, когдавторая часть кортежа - Some 'u.Таким образом, вопрос заключается в том, как сделать и то и другое: накапливать 't и фильтровать 'u?

Ответ заключается в объединении накопленного значения с Some 'u, что и делает f.Поэтому вам нужно сохранить кортеж в состоянии scan, а затем сохранить только вторую часть, используя choose и snd.

Это то, что вы ищете:

let accumulate (f:'t -> 'a -> 't * 'u option) t obs =
    obs
    |> Observable.scan (fun (acc, _) x -> f acc x) (t, None)
    |> Observable.choose snd

Понимание scan

scan - это функция, которая переносит изменяющееся состояние, передавая его функции вместе с серией значений.В частности, он может использоваться для накопления значений, например, int промежуточный итог:

let keepTotal obs =
    obs
    |> Observable.scan (fun total v -> total + v) 0

Это эквивалентно выполнению этого в императивном коде с изменяемым total:

let mutable total = 0

let keepTotal2 obs =
    obs
    |> Observable.map (fun v -> 
        total <- total + v
        total
    )

обратите внимание, что 2 версии имеют одинаковые элементы:

  • начальное значение: 0
  • функция аккумулятора: total + v

ИзКонечно, вторая версия, даже если она использует map, является плохим функциональным кодом, потому что она использует внешнюю изменяемую переменную, которая является большим NO NO.

Ваша первоначальная проблема могла быть решена таким же образом:

let accumulate2 (f:'t -> 'a -> 't * 'u option) t obs =
    let mutable acc = t
    obs
    |> Observable.choose (fun x ->
        let acc2, uOp = f acc x
        acc <- acc2
        uOp
    )

Несмотря на то, что в этой переменной используется изменяемая переменная, которая уродлива в функциональном программировании (и не нужна), она функционально нормальна, поскольку переменная acc является внутренней, и никакой код вне accumulate2 ее не видит.Тем не менее, все еще некрасиво.

0 голосов
/ 21 декабря 2018

Вы можете связать Observable.choose после вашего Observable.scan, чтобы получить правильную подпись типа

let accumulate (f:'t -> 'a -> 't * 'u option) t obs =
    obs
    |> Observable.scan (fun _ x -> snd (f t x)) None
    |> Observable.choose id
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...