wol*_*olf 4 f# functional-programming
我应该定义一个函数,它将返回 IObservable<'u>
accumulate : ('t -> 'a -> 't * 'u option) -> 't -> IObservable<'a> -> IObservable<'u>
Run Code Online (Sandbox Code Playgroud)
这样我的函数 ft obs' 将 obs 的可观察事件累积到类型为 't 的累加器中,并在观察事件 'a' 的 'snd (f acc a)' 计算结果为 'Some u' 时发出可观察事件 u。
到目前为止我已经实现了以下功能:
let accumulate (f:'t -> 'a -> 't * 'u option) t obs =
Observable.scan (fun _ x -> snd (f t x)) None obs
Run Code Online (Sandbox Code Playgroud)
我真的不明白这个可观察的扫描是如何工作的,在这种情况下我的函数返回 IObservable<'u option> 。我该如何解决这个问题?我走在正确的轨道上吗?
功能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
Run Code Online (Sandbox Code Playgroud)
scan
scan
是一个函数,通过将状态与一系列值一起传递给函数来承载变化的状态。特别是它可以用于累积值,例如int
运行总计:
let keepTotal obs =
obs
|> Observable.scan (fun total v -> total + v) 0
Run Code Online (Sandbox Code Playgroud)
这相当于在带有 mutable 的命令式代码中执行此操作total
:
let mutable total = 0
let keepTotal2 obs =
obs
|> Observable.map (fun v ->
total <- total + v
total
)
Run Code Online (Sandbox Code Playgroud)
请注意这两个版本如何具有相同的元素:
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
)
Run Code Online (Sandbox Code Playgroud)
尽管这个使用了一个可变变量,这在函数式编程中是丑陋的(并且不必要的),但它在功能上是可以的,因为该变量acc
是内部的,外部没有代码accumulate2
可以看到它。虽然还是丑。