FRP 中 EventStreams 的循环依赖关系
Circular dependencies of EventStreams in FRP
所有示例都使用 Ramda
作为_
(很清楚方法在示例上下文中的作用(,kefir
作为frp
(与培根中的 API 几乎相同.js(
我有一个流,描述位置的变化。
var xDelta = frp
.merge([
up.map(_.multiply(1)),
down.map(_.multiply(-1))
])
.sampledBy(frp.interval(10, 0))
.filter();
当我按下UP
键并-1
DOWN
时,它会发出+1
。
要获得位置,我scan
这个三角洲
var x = xDelta
.scan(_.add)
.toProperty(0);
这是预期的工作。但我想将x
的值从0
限制为1000
.
为了解决这个问题,我找到了两个解决方案:
更改
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。
我有
current
立场。和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
使用玻璃钢没有Controller
.因此Model
值应从其他Model
以声明方式计算。但是,如果Model1
从另一个相同的Model2
计算,那么Model2
从Model1
计算呢?
Model ----->
<----- Model
例如,两个具有碰撞检测功能的玩家:两个玩家都有position
和movement
。第一个玩家的movement
取决于第二个玩家的position
,反之亦然。
我仍然是所有这些东西的新手。经过多年的命令式编码,开始以声明式 FRP 风格思考并不容易。可能我错过了什么。
使用循环依赖可能是反模式的
是和不是。从实现这一点时遇到的困难中,您可以看到很难创建循环依赖项。尤其是以声明性的方式。但是,如果我们想使用纯声明式风格,我们可以看到循环依赖是无效的。例如,在Haskell中,您可以声明let x = x + 1
- 但它将评估为异常。
current
取决于delta
,delta
取决于current after applying
,current after applying
取决于current
如果你仔细观察,它不会。如果这是一个真正的循环依赖,current
从来没有任何价值。或者抛出了一个例外。
相反,current
确实取决于其以前的状态。这是FRP中众所周知的模式,步进器。从这个答案中获取:
e = ((+) <$> b) <@> einput
b = stepper 0 e
在不知道<$>
和<@>
究竟做什么的情况下,您可能知道事件e
和行为("属性"(b
如何取决于事件einput
。更好的是,我们可以声明性地扩展它们:
e = ((+) <$> bound) <@> einput
bound = (min 0) <$> (max 1000) <$> b
b = stepper 0 e
这基本上就是培根在scan
中所做的。不幸的是,它迫使您在单个回调函数中完成所有这些操作。
我还没有在任何 JS FRP 库1 中看到stepper
函数。在培根和开菲尔中,如果要实现此模式,则可能需要使用Bus
。我很高兴被证明是错的:-(
[1]:嗯,除了我因此而自己实现的那个(它还不能呈现(。但是使用Stepper
仍然感觉像跳过箍,因为JavaScript不支持递归声明。
有一个名为cyclejs的新框架/库,它完全按照你描述的循环机制工作,但在这种情况下,它适用于类似于Facebook新React的Webfrontend库。
基本思想是有一个模型,它是一个"状态"值流,一个呈现这些值的视图流,一个发出来自视图副作用(浏览器 DOM(的用户交互的用户交互流,以及一个"意图"流,它从用户创建高级事件并馈送到创建新值的模型中。
它仍处于早期开发阶段,但这是一个非常简洁的想法,到目前为止效果很好。
- 单元测试依赖关系没有被嘲笑
- FRP 中 EventStreams 的循环依赖关系
- 节点模块依赖关系
- 在Meteor中使用具有依赖关系的NPM包
- requirejs定义:嵌套依赖关系
- Django管道和javascript依赖关系
- 在Aurelia computeds中,当设置依赖关系时,如何声明对对象属性的依赖关系
- Npm未满足对等依赖关系
- Node.JS处理重复的可传递依赖关系
- 在Firefox上使用聚合物的javascript依赖关系的HTML导入困难;Safari
- 库中的匿名定义()模块's的依赖关系导致库损坏'的家属
- 使用Require.js按照依赖关系的顺序加载JavaScript
- GraphQL代码中的Javascript循环依赖关系
- 使用 grunt 检查单个 javascript 文件时如何解决依赖关系
- 与超类的依赖关系
- 在 Angular JS 业力测试中管理依赖关系
- 在 webpack 中管理 jQuery 插件依赖关系
- requirejs+bower,bower组件中的路径和依赖关系
- 预构建的NPM包:如何为用户省去依赖关系
- 聚合物种子-自定义元素依赖关系