编程语言
首页 > 编程语言> > javascript – FRP中EventStreams的循环依赖关系

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 on delta, delta depends on current after applying,
current after applying depends on current

如果你仔细观察,它就不会.如果这是一个真正的循环依赖,那么当前从来没有任何价值.或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