如何在没有状态变量的情况下实现逻辑
How to implement the logic without state variables
有两个可观察对象:第一个名为 activator
的对象发出布尔值。第二个名为 signaler
的事件发出 void 事件。有一个函数 f()
必须在下一个条件下调用:
如果 activator
的最后一个事件是 true
,并且 signaler
的事件来了,调用 f()
。否则(最后一个 activator
的事件是 false
,或者 activator
还没有发出任何东西),“记住”signaler
发送了事件。一旦 activator
发出 true
,调用 f()
并清除“记住”标志。
示例:
let activator = PublishRelay<Bool>()
let signaler = PublishRelay<Void>()
signaler.accept(()) // activator not emitted yet, just remember that signal came
activator.accept(true) // go to active state. signal is waiting. call f()
signaler.accept(()) // already activated. call f()
activator.accept(false)// go to inactive state
activator.accept(true) // go to active state.
signaler.accept(()) // call f()
activator.accept(false)// go to inactive state
signaler.accept(()) // inactive state, remember that signal came
signaler.accept(()) // still inactive state, remember that signal came
activator.accept(true) // go to active state. there is signal waiting. call f().
signaler.accept(()) // active state. call f().
我可以使用两个状态变量 _isActive
和 _waiting
实现所需的行为:
var _isActive = false
var _waiting = false
activator.bind { isActive in
self._isActive = isActive
if isActive && self._waiting {
f()
self._waiting = false
}
}.disposed(by: _bag)
signaler.bind {
if self._isActive {
f()
} else {
self._waiting = true
}
}.disposed(by: _bag)
问题是:我可以在没有状态变量的情况下仅通过反应运算符来实现它吗?
你需要一个状态机,但你可以包含状态,这样你就不会离开 monad...像这样:
func example(activator: Observable<Bool>, signaler: Observable<Void>) -> Observable<Void> {
enum Action {
case signal
case active(Bool)
}
return Observable.merge(signaler.map(to: Action.signal), activator.map(Action.active))
.scan((isWaiting: false, isActive: false, fire: Void?.none)) { state, action in
switch action {
case .signal:
if state.isActive {
return (state.isWaiting, state.isActive, ())
}
else {
return (true, state.isActive, .none)
}
case .active(let active):
if active && state.isWaiting {
return (false, active, ())
}
else {
return (state.isWaiting, active, .none)
}
}
}
.compactMap { [=10=].fire }
}
请注意扫描闭包内部的逻辑与您已有的外部逻辑是如何相同的。有了上面的内容,你现在可以做这样的事情了:
let activator = PublishRelay<Bool>()
let signaler = PublishRelay<Void>()
example(
activator: activator.asObservable(),
signaler: signaler.asObservable()
)
.bind(onNext: f)
最后,作为奖励。这是一个证明它有效的单元测试:
class RxSandboxTests: XCTestCase {
func test() {
let scheduler = TestScheduler(initialClock: 0)
let activator = scheduler.createColdObservable([.next(20, true), .next(40, false), .next(50, true), .next(70, false), .next(100, true)])
let signaler = scheduler.createColdObservable([.next(10, ()), .next(30, ()), .next(60, ()), .next(80, ()), .next(90, ()), .next(110, ())])
let result = scheduler.start {
example(activator: activator.asObservable(), signaler: signaler.asObservable())
}
XCTAssertEqual(result.events.map { [=12=].time }, [220, 230, 260, 300, 310])
}
}
有两个可观察对象:第一个名为 activator
的对象发出布尔值。第二个名为 signaler
的事件发出 void 事件。有一个函数 f()
必须在下一个条件下调用:
如果 activator
的最后一个事件是 true
,并且 signaler
的事件来了,调用 f()
。否则(最后一个 activator
的事件是 false
,或者 activator
还没有发出任何东西),“记住”signaler
发送了事件。一旦 activator
发出 true
,调用 f()
并清除“记住”标志。
示例:
let activator = PublishRelay<Bool>()
let signaler = PublishRelay<Void>()
signaler.accept(()) // activator not emitted yet, just remember that signal came
activator.accept(true) // go to active state. signal is waiting. call f()
signaler.accept(()) // already activated. call f()
activator.accept(false)// go to inactive state
activator.accept(true) // go to active state.
signaler.accept(()) // call f()
activator.accept(false)// go to inactive state
signaler.accept(()) // inactive state, remember that signal came
signaler.accept(()) // still inactive state, remember that signal came
activator.accept(true) // go to active state. there is signal waiting. call f().
signaler.accept(()) // active state. call f().
我可以使用两个状态变量 _isActive
和 _waiting
实现所需的行为:
var _isActive = false
var _waiting = false
activator.bind { isActive in
self._isActive = isActive
if isActive && self._waiting {
f()
self._waiting = false
}
}.disposed(by: _bag)
signaler.bind {
if self._isActive {
f()
} else {
self._waiting = true
}
}.disposed(by: _bag)
问题是:我可以在没有状态变量的情况下仅通过反应运算符来实现它吗?
你需要一个状态机,但你可以包含状态,这样你就不会离开 monad...像这样:
func example(activator: Observable<Bool>, signaler: Observable<Void>) -> Observable<Void> {
enum Action {
case signal
case active(Bool)
}
return Observable.merge(signaler.map(to: Action.signal), activator.map(Action.active))
.scan((isWaiting: false, isActive: false, fire: Void?.none)) { state, action in
switch action {
case .signal:
if state.isActive {
return (state.isWaiting, state.isActive, ())
}
else {
return (true, state.isActive, .none)
}
case .active(let active):
if active && state.isWaiting {
return (false, active, ())
}
else {
return (state.isWaiting, active, .none)
}
}
}
.compactMap { [=10=].fire }
}
请注意扫描闭包内部的逻辑与您已有的外部逻辑是如何相同的。有了上面的内容,你现在可以做这样的事情了:
let activator = PublishRelay<Bool>()
let signaler = PublishRelay<Void>()
example(
activator: activator.asObservable(),
signaler: signaler.asObservable()
)
.bind(onNext: f)
最后,作为奖励。这是一个证明它有效的单元测试:
class RxSandboxTests: XCTestCase {
func test() {
let scheduler = TestScheduler(initialClock: 0)
let activator = scheduler.createColdObservable([.next(20, true), .next(40, false), .next(50, true), .next(70, false), .next(100, true)])
let signaler = scheduler.createColdObservable([.next(10, ()), .next(30, ()), .next(60, ()), .next(80, ()), .next(90, ()), .next(110, ())])
let result = scheduler.start {
example(activator: activator.asObservable(), signaler: signaler.asObservable())
}
XCTAssertEqual(result.events.map { [=12=].time }, [220, 230, 260, 300, 310])
}
}