gpt4 book ai didi

javascript - FRP 中 EventStreams 的循环依赖

转载 作者:塔克拉玛干 更新时间:2023-11-02 20:33:44 26 4
gpt4 key购买 nike

所有示例都使用 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

为了解决这个问题,我找到了两个解决方案:

  1. scan中改变函数

    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。

  1. 我有当前职位。和 delta。我想将 delta 应用于 current,前提是 current after applying 不会超出限制。

    • current 取决于 delta
    • delta 取决于 current after applying
    • current after applying 取决于current

    所以看起来像循环依赖。但我使用 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,没有 Controller。因此,Model 值应该根据其他 Model 以声明方式计算。但是如果 Model1 从另一个相同的 Model2 计算,那么 Model2Model1 计算呢?

Model ----->
<----- Model

例如两个具有碰撞检测的玩家:两个玩家都有positionmovement。而第一个玩家的移动取决于第二个玩家的位置,反之亦然。

我在这方面还是个新手。经过多年的命令式编码,开始以声明式 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

如果你仔细观察,它不会。如果这是一个真正的循环依赖,current从来没有任何值(value)。或者 threw an exception .

相反,current确实取决于它之前的状态。这是 FRP 中众所周知的模式,即 stepper。取自this answer :

e = ((+) <$> b) <@> einput
b = stepper 0 e

不知道是什么<$><@>确实如此,你大概可以说出事件是如何发生的e和行为(“属性(property)”)b取决于事件einput .更好的是,我们可以声明性地扩展它们:

e = ((+) <$> bound) <@> einput
bound = (min 0) <$> (max 1000) <$> b
b = stepper 0 e

培根在 scan 中基本上就是这么做的.不幸的是,它迫使您在一个回调函数中完成所有这些。

我还没有看到 stepper在任何 JS FRP 库中运行1。在培根和开菲尔中,您可能必须使用 Bus如果你想实现这个模式。 我很高兴被证明是错误的:-)

[1]:好吧,除了我自己实现的那个(它还不能展示)。但是使用 Stepper仍然感觉像在跳圈,因为 JavaScript 不支持递归声明。

关于javascript - FRP 中 EventStreams 的循环依赖,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29478305/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com