我应该定义一个函数,该函数将返回IObservable <'u>
accumulate : ('t -> 'a -> 't * 'u option) -> 't -> IObservable<'a> -> IObservable<'u>
因此,我的函数ft obs'将obs的可观察事件累积到类型为't的累加器中,并且当'snd(f acc a)'对观察到的事件'a'求值为'Some u'时,发出可观察事件u
到目前为止,我已经实现了以下功能:
let accumulate (f:'t -> 'a -> 't * 'u option) t obs =
Observable.scan (fun _ x -> snd (f t x)) None obs
我真的不明白这种可观察的扫描方式是如何工作的,在这种情况下,我的函数返回IObservable <'u选项>。我该如何解决?我在正确的轨道上吗?
答案 0 :(得分:2)
函数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
)
注意两个版本如何具有相同的元素:
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
之外的任何代码都看不到它。仍然很丑。
答案 1 :(得分:1)
您可以在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