Rx java RxJava2中的功能性反应运算符,它从以前的状态和附加输入生成新状态
我正在做一个小展示,它应该演示如何使用函数式反应式编程(特别是RxJava2)以纯函数式的方式编写交互式程序 我的目标是创建一个简单的交互式游戏。游戏的主要功能(我们称之为下一个游戏状态,简称NGS)获取游戏的当前状态、用户输入以及随机数,并根据这三个输入计算游戏的下一个状态。到目前为止相当简单。用户输入和随机数是可流动的,它们是从可流动的或通过生成器创建的。我设想游戏状态本身也是可流动的(但我可能错了) 我正在努力寻找正确的函数反应运算符,将函数应用于三个输入并生成游戏的下一个状态。起初,我认为Rx java RxJava2中的功能性反应运算符,它从以前的状态和附加输入生成新状态,rx-java,reactive-programming,rx-java2,purely-functional,Rx Java,Reactive Programming,Rx Java2,Purely Functional,我正在做一个小展示,它应该演示如何使用函数式反应式编程(特别是RxJava2)以纯函数式的方式编写交互式程序 我的目标是创建一个简单的交互式游戏。游戏的主要功能(我们称之为下一个游戏状态,简称NGS)获取游戏的当前状态、用户输入以及随机数,并根据这三个输入计算游戏的下一个状态。到目前为止相当简单。用户输入和随机数是可流动的,它们是从可流动的或通过生成器创建的。我设想游戏状态本身也是可流动的(但我可能错了) 我正在努力寻找正确的函数反应运算符,将函数应用于三个输入并生成游戏的下一个状态。起初,我认
Flowable.zip(source1、source2、source3、zipper)
是正确的操作:它可以接受这三个流,并通过NGS函数将其组合成一个新的Flowable。不幸的是,这并不能解释这样一个事实,即生成的Flowable本身需要成为zip操作的输入之一,这似乎是一个不可能的设置。我的下一个想法是使用Flowable.generate
,但我需要来自其他Flowable
的两个额外输入来计算下一个状态,并且无法将它们输入generate
操作符
简而言之,我试图实现类似于此(伪)大理石图的东西:
Game --------------(0)-------------(1)-----------------(2)-----------------(3)---
State \ _______ / \ _______ / \ _______ /
\_| |_/ \_____| |_/ \_____| |_/
| Next | | Next | | Next |
_____| Game | _____| Game | _____| Game |
/ __| State | / __| State | / __| State |
/ / '_______' / / '_______' / / '_______'
/ / / / / /
User / / / / / /
Input --------o---/---------------o---/---------------o---/--------------------
/ / /
Random / / /
Number --------o-------------------o-------------------o---------------------------
我承认,最上面的一行有点不标准,但这是我最好的设想,从最初的游戏状态(0)
,每个连续的游戏状态(1)
,(2)
,(3)
,等等开始,都是从先前的游戏状态加上额外的输入创建的
我意识到,可能有一种相对简单的方法可以通过发射器
或者在下游订阅者内部实现这一点,但本练习的目标之一是表明这可以完全解决,而不会产生副作用或无效
方法
那么,长话短说,是否存在支持这种行为的现有操作符?如果没有,创建一个会涉及到什么?或者是否有其他解决方案使用稍微不同的总体设置?假设
用户输入和随机数是两个独立的流,它们可以在任何时候自己发出
解决方案
长话短说,是否存在支持此行为的现有运算符
是的,有。您可以使用#scan(seed,{current,upstream->newValue}
import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.functions.BiFunction
import io.reactivex.rxjava3.processors.PublishProcessor
import org.junit.jupiter.api.Test
class So65589721 {
@Test
fun `65589721`() {
val userInput = PublishProcessor.create<String>()
val randomNumber = PublishProcessor.create<Int>()
val scan = Flowable.combineLatest(userInput, randomNumber, BiFunction<String, Int, Pair<String, Int>> { u, r ->
u to r
}).scan<GameState>(GameState.State1, { currentState, currentInputTuple ->
// calculate new state from `currentState` and combined pair
GameState.State3(currentInputTuple.first, currentInputTuple.second)
})
val test = scan.test()
randomNumber.offer(42)
userInput.offer("input1")
userInput.offer("input2")
test
.assertValues(GameState.State1, GameState.State3("input1", 42), GameState.State3("input2", 42))
}
interface GameState {
object State1 : GameState
data class State3(val value: String, val random: Int) : GameState
}
}
import io.reactivex.rxjava3.core.Flowable
导入io.reactivex.rxjava3.functions.BiFunction
导入io.reactivex.rxjava3.processors.PublishProcessor
导入org.junit.jupiter.api.Test
So65589721类{
@试验
有趣的‘65589721’(){
val userInput=PublishProcessor.create()
val randomNumber=PublishProcessor.create()
val scan=Flowable.CombineTest(用户输入,随机数,双功能{u,r->
u到r
}).scan(GameState.State1,{currentState,currentInputUple->
//从“currentState”和组合对计算新状态
GameState.State3(currentInputUple.first,currentInputUple.second)
})
val test=scan.test()
随机数字。报价(42)
userInput.offer(“input1”)
userInput.offer(“input2”)
测试
.assertValues(GameState.State1,GameState.State3(“input1”,42),GameState.State3(“input2”,42))
}
界面游戏状态{
对象状态1:游戏状态
数据类State3(val-value:String,val-random:Int):游戏状态
}
}
此示例演示如何使用scan
从给定的输入对和当前状态计算新状态。使用scan
可以安全地“保持”状态,而不会将其表现为副作用
注
- 传递给
的scan
种子值将作为订阅时的第一个值发出
zip
,然后是一个scan
:zip(随机,响应,输入.工厂::创建)。扫描(State.FACTORY.create(1100,可选的.empty()),实例::next)
我的完整工作示例(Java 8+RxJava 2)可以在这里找到: