javascript – FRP中EventStreams的循环依赖关系
作者:互联网
所有示例都使用Ramda作为_(很清楚示例上下文中有哪些方法)和kefir作为frp(几乎与bacon.js中的API相同)
我有一个描述位置变化的流.
var xDelta = frp
.merge([
up.map(_.multiply(1)),
down.map(_.multiply(-1))
])
.sampledBy(frp.interval(10, 0))
.filter();
当我按下UP键时它会发出1,而在DOWN时会发出-1.
为了获得位置,我扫描这个三角洲
var x = xDelta
.scan(_.add)
.toProperty(0);
这是按预期工作的.但我想将x的值从0限制为1000.
为解决这个问题,我找到了两个解决方案
>在扫描中更改功能
var x = xDelta.scan(function (prev, next) {
var newPosition = prev + next;
if (newPosition < 0 && next < 0) {
return prev;
}
if (newPosition > 1000 && next > 0) {
return prev;
}
return newPosition;
}, 0);
它看起来很好,但后来,随着新规则的引入,这种方法将会增长.所以我的意思是它看起来不具有组合性和FRPy.
>我有现在的职位.和三角洲.我想将delta应用于当前,只有当应用后的电流不会超出限制时.
>电流取决于三角洲
> delta取决于应用后的电流
>应用后的电流取决于电流
所以它看起来像循环依赖.但我用flatMap解决了它.
var xDelta = frp
.merge([
up.map(_.multiply(1)),
down.map(_.multiply(-1))
])
.sampledBy(frp.interval(10, 0))
.filter();
var possibleNewPlace = xDelta
.flatMap(function (delta) {
return x
.take(1)
.map(_.add(delta));
});
var outOfLeftBoundFilter = possibleNewPlace
.map(_.lte(0))
.combine(xDelta.map(_.lte(0)), _.or);
var outOfRightBoundFilter = possibleNewPlace
.map(_.gte(1000))
.combine(xDelta.map(_.gte(0)), _.or);
var outOfBoundFilter = frp
.combine([
outOfLeftBoundFilter,
outOfRightBoundFilter
], _.and);
var x = xDelta
.filterBy(outOfBoundFilter)
.scan(_.add)
.toProperty(0);
您可以在iofjuupasli/capture-the-sheep-frp看到完整的代码示例
它正在运行演示gh-pages
它可以工作,但使用循环依赖可能是反模式.
有没有更好的方法来解决FRP中的循环依赖?
第二个更普遍的问题
使用Controller,可以从两个Model中读取一些值,并根据它的值更新它们.
所以依赖关系看起来像:
---> Model
Controller ---|
---> Model
使用FRP,没有控制器.所以模型值应该从其他模型中以声明方式计算.但是,如果Model1从另一个Model2计算得到的相同,那么Model2从Model1计算出来呢?
Model ----->
<----- Model
例如,两个有碰撞检测的玩家:两个玩家都有位置和移动.第一个玩家的移动取决于第二个玩家的位置,反之亦然.
我仍然是所有这些东西的新手.经过多年的命令式编码后,开始思考声明性FRP风格并不容易.可能我错过了一些东西.
解决方法:
using circular dependencies is probably anti-pattern
是的,不是.从实现这一点的困难中,您可以看到很难创建循环依赖.特别是以声明的方式.但是,如果我们想使用纯声明式样式,我们可以看到循环依赖是无效的.例如.在Haskell中,您可以声明let x = x 1 – 但它将评估为异常.
current
depends ondelta
,delta
depends oncurrent after applying
,
current after applying
depends oncurrent
如果你仔细观察,它就不会.如果这是一个真正的循环依赖,那么当前从来没有任何价值.或threw an exception.
相反,电流确实取决于其先前的状态.这是步进式FRP中众所周知的模式.从this answer开始:
e = ((+) <$> b) <@> einput
b = stepper 0 e
不知道什么< $>和< @>确切地说,您可以告诉事件e和行为(“属性”)b如何依赖于事件输入.更好的是,我们可以声明性地扩展它们:
e = ((+) <$> bound) <@> einput
bound = (min 0) <$> (max 1000) <$> b
b = stepper 0 e
这基本上就是培根在扫描中的作用.不幸的是,它迫使你在一个回调函数中完成所有这些工作.
我还没有在任何JS FRP库1中看到过步进功能.在Bacon和Kefir,如果你想实现这种模式,你可能不得不使用总线.我很高兴被证明是错的:-)
[1]:嗯,除了我自己实现的那个(它还没有表现).但是使用Stepper仍然感觉像是跳过了箍,因为JavaScript不支持递归声明.
标签:javascript,circular-dependency,frp,ramda-js,bacon-js 来源: https://codeday.me/bug/20190609/1205350.html