本文介绍了反应性香蕉能否处理网络中的周期?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 guiState ::离散状态 guiState = stepperD(GuiState []) $ union(mkGuiState< $>更改模型)evtAutoLayout evtAutoLayout :: Event GuiState evtAutoLayout = fmap fromJust。 filterE isJust。 fmap autoLayout $ changes guiState 你可以看到evtAutoLayout馈入到馈入的guiState中evtAutoLayout - 那里有一个周期。这是故意的。 Auto 布局调整gui状态直到它达到均衡,然后它返回Nothing,所以它应该停止循环。当然,新的模型更改可以再次启动它。 然而,当我们将它们放在一起时,我们会在$ b上遇到无限循环$ b编译函数调用。即使autoLayout = Nothing,它在编译期间仍然会导致堆栈溢出。 如果我在guiState中删除union call并从中删除evtAutoLayout, ... guiState ::离散状态状态 guiState = stepperD(GuiState [])$ mkGuiState< $> ;更改模型 它可以正常工作。 有什么建议吗?解决方案问题 不仅有一个,而且有三个答案。简短的答案是:1.一般不会,2.有时是,3.有解决方法是。 这里的答案很长。 反应式香蕉的语义不支持定义 事件直接根据它自己。 这是Conal Elliott在他最初的FRP语义中做出的决定,我决定坚持。它的主要好处是语义仍然非常简单,您可以始终认为,根据 类型Behavior a = Time - > a 类型Event a = [(Time,a)] 我提供了一个模块 Reactive.Banana.Model 几乎精确地实现了这个模型,你可以查询它的源代码,以了解有关反应性香蕉语义的任何问题。特别是,你可以用它来推论你的例子:用笔和笔来计算。或者在GHCi中使用它(有些模拟数据)会告诉你 evtAutoLayout 的值等于 _ | _ ,即未定义。 后者可能会令人惊讶,但正如您所写,该示例确实未定义:只有在 evtAutoLayout 事件发生,但只有当你知道GUI状态是否改变时才会发生,反过来等等。你总是需要通过插入一个小延迟来打破扼杀反馈循环 / em>的。不幸的是,反应香蕉目前不提供插入小延迟的方法,主要是因为我不知道如何描述 [(Time,a)] 模型的方式允许递归。 (但请参阅答案3)。 可以并鼓励根据以下条件定义 Event 再次引用该事件的行为>。换句话说,只要你经历一个行为就允许递归。 一个简单的例子是 import Reactive.Banana.Model filterRising ::(FRP f,Ord a)=>事件f a - >事件fa filterRising eInput = eOutput 其中 eOutput = filterApply(更大< $>行为)eInput 行为=步进器Nothing(Just< $> eOutput) 更大Nothing _ = True 更大(仅x)y = x 示例:: [(Time,Int)] 示例= interpretTime filterRising $ zip [1 ..] [2,1,5,4,8,9,7] - example = [(1.0,2),(3.0,5),(5.0,8),(6.0,9)] 给定一个事件流,函数 filterRising 只返回那些大于先前返回的事件。这在中暗示 stepper 函数的文档。 然而,这可能不是您想要的递归类型。 然而,在反应式香蕉中插入很小的延迟是可能的,它不是核心库的一部分,因此没有任何保证语义。另外,您确实需要事件循环中的一些支持。 例如,您可以使用wxTimer在事件处理完成后立即安排事件当前的一个。 Wave.hs 示例演示了递归使用与反应香蕉的wxTimer。我不太清楚将定时器间隔设置为 0 时会发生什么情况,但它可能执行得太早。您可能需要尝试一下才能找到一个好的解决方案。 希望有所帮助;随时要求澄清,例子等。 披露:我是反应性香蕉图书馆的作者。 We have code like this: guiState :: Discrete GuiState guiState = stepperD (GuiState []) $ union (mkGuiState <$> changes model) evtAutoLayout evtAutoLayout :: Event GuiState evtAutoLayout = fmap fromJust . filterE isJust . fmap autoLayout $ changes guiStateYou can see that evtAutoLayout feeds into guiState which feeds intoevtAutoLayout--so there is a cycle there. This is deliberate. Autolayout adjusts the gui state until it reaches an equilibrium and thenit returns Nothing and so it should stop the loop. A new model changecan kick it off again, of course.When we put this together, though, we run into an infinite loop on thecompile function call. Even if autoLayout = Nothing, it still results in a stack overflow during compile.If I remove the union call in guiState and remove evtAutoLayout out ofthe picture... guiState :: Discrete GuiState guiState = stepperD (GuiState []) $ mkGuiState <$> changes modelit works fine.Any suggestions? 解决方案 The questionhas not only one, but three answers. The short answers are: 1. generally no, 2. sometimes yes, 3. with workaround yes.Here the long answers.The semantics of reactive-banana do not support defining an Event directly in terms of itself.This is a decision that Conal Elliott made in his original FRP semantics and I've decided to stick to it. Its main benefit is that the semantics remain very simple, you can always think in terms oftype Behavior a = Time -> atype Event a = [(Time,a)]I have provided a module Reactive.Banana.Model that implements almost precisely this model, you can consult its source code for any questions concerning the semantics of reactive-banana. In particular, you can use it to reason about your example: a calculation with pen & paper or trying it in GHCi (with some mock data) will tell you that the value evtAutoLayout is equal to _|_, i.e. undefined.The latter may be surprising, but as you wrote it, the example is indeed undefined: the GUI state only changes if an evtAutoLayout event happens, but it can only happen if you know whether the GUI state changes, which in turn, etc. You always need to break the strangulating feedback loop by inserting a small delay. Unfortunately, reactive-banana doesn't currently offer a way to insert small delays, mainly because I don't know how to describe small delays in terms of the [(Time,a)] model in a way that allows recursion. (But see answer 3.)It is possible and encouraged to define an Event in terms of a Behavior that refers to the Event again. In other words, recursion is allowed as long as you go through a Behavior.A simple example would beimport Reactive.Banana.ModelfilterRising :: (FRP f, Ord a) => Event f a -> Event f afilterRising eInput = eOutput where eOutput = filterApply (greater <$> behavior) eInput behavior = stepper Nothing (Just <$> eOutput) greater Nothing _ = True greater (Just x) y = x < yexample :: [(Time,Int)]example = interpretTime filterRising $ zip [1..] [2,1,5,4,8,9,7]-- example = [(1.0, 2),(3.0, 5),(5.0, 8),(6.0, 9)]Given an event stream, the function filterRising returns only those events that are greater than the previously returned. This is hinted at in the documentation for the stepper function.However, this is probably not the kind of recursion you desire.Still, it is possible to insert small delays in reactive-banana, it's just not part of the core library and hence doesn't come with any guaranteed semantics. Also, you do need some support from your event loop to do that.For instance, you can use a wxTimer to schedule an event to happen right after you've handled the current one. The Wave.hs example demonstrates the recursive use of a wxTimer with reactive-banana. I don't quite know what happens when you set the timer interval to 0, though, it might execute too early. You probably have to experiment a bit to find a good solution.Hope that helps; feel free to ask for clarifications, examples, etc.Disclosure: I'm the author of the reactive-banana library. 这篇关于反应性香蕉能否处理网络中的周期?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 09-14 09:05