【问题标题】:Using netwire's periodic vs. at wires使用 netwire 的周期性与在线
【发布时间】:2014-07-06 00:34:49
【问题描述】:

我正在尝试在 Haskell 中编写一个用于实时交互式图形的框架。我一直试图通过使用 Netwire 5 来处理事情,但我似乎没有很好地处理事情如何“相互依赖”。 For example,下面的代码应该在切换到 (val + 1) 之前产生 val 两秒钟,然后无限期地继续。

someWire :: Num a => a -> Wire s e m a a
someWire x = (-->) ((pure x) &&& (periodic 2) >>> until) (someWire (x + 1))

但是,这会造成某种内存泄漏,导致我的程序停止并一直分配内存,直到系统崩溃。或者,这个定义

someWire :: Num a => a -> Wire s e m a a
someWire x = (-->) ((pure x) &&& (at 2) >>> until) (someWire (x + 1))

按照我期望的方式运行:从val 开始计数,值每两秒更改一次。有人可以解释一下这种行为吗?

【问题讨论】:

  • 您的someWires 不进行类型检查。我假设你的意思是例如((pure val &&& (periodic 2)) >>> until) --> (someWire (val + 1)).
  • 谢谢,我修好了。这里有一个更好的完整崩溃示例:gist.github.com/Mokosha/2b65655450981cfded65

标签: haskell functional-programming frp netwire


【解决方案1】:

关键的见解是periodic立即产生一个事件。

因此,当我们从这条线产生一个值时,我们必须将它评估为以下内容:

someWire x
(-->) ((pure x) &&& (periodic 2) >>> until) (someWire (x + 1))
(-->) (pure (x, Event _) >>> until) (someWire (x + 1))
(-->) *inhibition* (someWire (x + 1))
someWire (x + 1)

由于这不是尾递归,因此不允许垃圾收集器清理为线路分配的先前实例,并且内存不足(而不是无限循环)。

【讨论】:

  • +1 你的问题比我一开始想的要微妙。
【解决方案2】:

让我们看看工作版本:

import Control.Wire hiding (until)
import qualified Control.Wire (until) as W

someWire :: Num a => a -> Wire s e m a a
someWire x = (-->) (pure x &&& at 2 >>> W.until) (someWire (x + 1))

-- To test in GHCi: testWire clockSession_ $ someWire 3 

Wires 是Arrows,因此如果我们能够按照箭头组合器的操作进行操作,就会更容易理解发生了什么。

(&&&) :: Arrow a => a b c -> a b c' -> a b (c, c')

(&&&) 构建一个箭头,将其输入(在我们的例子中,可以被认为是驱动事件的心跳)分成一对,将一个箭头应用于每个组件。在这里,我们用一个总是产生x 的箭头和一个返回将在两秒内发生的事件的箭头进行分叉。

(>>>) :: Category cat => cat a b -> cat b c -> cat a c

(>>>) 只是箭头的组合(ArrowCategory 的子类),按先到后后的顺序编写。所以我们将pure x &&& at 2 的输出发送到W.until

W.until :: Monoid e => Wire s e m (a, Event b) a

W.until 组合将产生与事件配对的值的箭头(例如pure x &&& at 2)转换为在事件发生之前产生值的箭头。一旦发生这种情况,我们使用(-->) 切换到另一条线。

现在应该更容易理解为什么不能使用periodic 而不是atat 仅在一个瞬间发生,而 periodic 继续发生,因此 W.until 永远不会终止。 (如果您深入研究源代码,您会发现W.until 做了类似于折叠事件发生的事情,这与直观的解释相符。)

【讨论】:

  • Hrm,这对我来说仍然没有意义。如果我用periodic 2 >>> (delay NoEvent) 替换periodic 2,那么一切都会像我预期的那样运行(它会每隔一个线步切换一次)。 W.until 在文档中描述的“折叠”只是为了消耗事件的值(如果它发生了)。我相信内存消耗与我不理解的电线的时空依赖性有关......
猜你喜欢
  • 2015-05-09
  • 2013-09-02
  • 2020-07-13
  • 2015-05-03
  • 1970-01-01
  • 2014-06-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多