1

考虑以下代码:

-- this defines what our 'state' will be
data Direction = North | East | South | West deriving (Eq, Show, Enum)
data State = State Int Bool Direction deriving (Show)

initialState :: State
initialState = State 0 True North

-- a simple routine to change a state and count the number of
-- changes
nextState :: State -> State
nextState (State i _ West) = State (i+1) False South
nextState (State i _ North) = State (i+1) True East
nextState (State i b s) = State i b $ (if b then succ else pred) s

-- a wire with local state
stateWire :: Wire s () m a State
stateWire = stateWireFrom initialState
  where
    stateWireFrom s = mkSFN $ \_ -> (nextState s, stateWireFrom (nextState s))

-- let's run the wire!
main = testWire clockSession_ stateWire 

可以想象,testWire它将尽可能快地运行电线并将输出打印到屏幕上。但是,如果我想每 2 秒运行一次电线怎么办?查看文档,periodic可能是解决方案:

-- Since periodic generates events, asSoonAs is used to 'unwrap' the Event
main = testWire clockSession_ (asSoonAs . periodic 2 . stateWire)

几乎可以工作。输出似乎是静态的大约 2 秒,但是当它更新时,很明显,当输出停止时,电线正在运行。也许我应该反过来做?

-- Now, this does make more sense to me...
main = testWire clockSession_ (stateWire . periodic 2)

但是,最终结果与我的第一次尝试完全一样。我在这里想念什么?

编辑:请参阅此答案以获取已接受答案的(劣质)替代方案。

4

2 回答 2

1

问题似乎是您将您的 stateWire视为连续电线,但它实际上应该是事件电线本身。假设我正确理解了您的意图,它可能应该是accumE (flip $ const nextState) initialState- 请参阅accumE的事件文档- 然后您可以像这样使用它:

stateWire . periodic 2(反过来是行不通的)。

您的原始版本不起作用的原因是periodic没有事件时不会抑制,它只会产生一个NoEvent值。而且由于您的 stateWire 只是忽略其输入,因此当周期性线在前面时,是否产生事件对其没有任何影响,而将周期性线放在后面只是意味着“定期捕获当前的快照state',这也不是你想要的。

注意:上一段中的“前”和“后”是指执行顺序,而不是源代码中的布局,如果使用.组合器则相反。

于 2015-03-02T21:58:07.150 回答
1

作为接受的答案的替代方案,也可以过滤掉NoEvent,而无需更改电线:

main = testWire clockSession_ (stateWire . when occurred . periodic 2)

在这种情况下,电线将改变状态,抑制 2 秒,然后再次改变。

另一个(接受的)答案不同:电线将改变状态,然后继续产生相同的结果 2 秒,然后再次改变它。

于 2015-03-03T18:29:29.100 回答