如何在没有状态变量的情况下实现逻辑

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])
    }
}