Rx java RxJava2中的功能性反应运算符,它从以前的状态和附加输入生成新状态

Rx java RxJava2中的功能性反应运算符,它从以前的状态和附加输入生成新状态,rx-java,reactive-programming,rx-java2,purely-functional,Rx Java,Reactive Programming,Rx Java2,Purely Functional,我正在做一个小展示,它应该演示如何使用函数式反应式编程(特别是RxJava2)以纯函数式的方式编写交互式程序 我的目标是创建一个简单的交互式游戏。游戏的主要功能(我们称之为下一个游戏状态,简称NGS)获取游戏的当前状态、用户输入以及随机数,并根据这三个输入计算游戏的下一个状态。到目前为止相当简单。用户输入和随机数是可流动的,它们是从可流动的或通过生成器创建的。我设想游戏状态本身也是可流动的(但我可能错了) 我正在努力寻找正确的函数反应运算符,将函数应用于三个输入并生成游戏的下一个状态。起初,我认

我正在做一个小展示,它应该演示如何使用函数式反应式编程(特别是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
    种子值将作为订阅时的第一个值发出

游戏状态是否取决于用户输入和随机数,或者游戏状态是否可能以某种方式自行改变?我猜,游戏状态总是从种子状态加上一些输入值派生出来。当输入值(随机数,用户输入)时更改后,将计算一个新的游戏状态。@HansWurst游戏状态确实总是从以前的游戏状态和一些附加输入值派生出来;游戏状态本身不会更改,它只能根据用户输入而更改。是否需要将用户输入和随机数成对处理?例如(newInput1,randomNumber1,curr_状态)->新状态;(newInput2,randomNumber2,curr_状态)->新状态。或者有可能在没有新随机数的情况下发出输入?例如(newInput1,randomNumber1,curr_状态)->新状态;(newInput2,randomNumber1,curr_状态)->新状态。此示例显示,输入总是与随机数的最新值组合(CombineTest/withLatestFrom)@HansWurst是的,用户输入和随机数总是成对出现的。感谢您的详细回答,@HansWurst。在我的特殊情况下,具体的解决方案是一个
zip
,然后是一个
scan
zip(随机,响应,输入.工厂::创建)。扫描(State.FACTORY.create(1100,可选的.empty()),实例::next)
我的完整工作示例(Java 8+RxJava 2)可以在这里找到: