Функция 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
ее не видит.Тем не менее, все еще некрасиво.