gpt4 book ai didi

haskell - 正确使用网线(五)

转载 作者:行者123 更新时间:2023-12-04 21:40:06 25 4
gpt4 key购买 nike

一段时间以来,我一直想尝试 FRP,昨天我终于硬着头皮尝试了,开始使用 Netwire 5(这本身是一个相当随意的选择,但我必须从某个地方开始!)。我已经设法达到了“有效代码”的地步,但我注意到了一些模式,我不确定它们是如何使用库的一部分,或者它们是否是我的症状我在某处做错了什么。

我从 this tutorial 开始,这足以让我轻松启动和运行——我现在有一个由简单的“递增数字”线控制的旋转立方体:

spin :: (HasTime t s, Monad m) => Wire s e m a GL.GLfloat
spin = integral 0 . 5

应用程序将在按下“Esc”时退出,利用 netwire-input-glfw 中提供的电线:
shouldQuit :: (Monoid e, Functor m, Monad m) => Wire s e (GLFWInputT m) a a
shouldQuit = keyPressed GLFW.Key'Escape

它们之间的一个重要区别是 spin 从不禁止——它应该总是返回一些值——而 shouldQuit 一直禁止;直到实际按下该键,在这种情况下我退出了应用程序。

让我不安的是我最终不得不使用这些电线的方式。现在,它看起来像这样:
(wt', spinWire') <- stepWire spinWire st $ Right undefined
((qt', quitWire'), inp'') <- runStateT (stepWire quitWire st $ Right undefined) inp'

case (qt', wt') of
(Right _, _) -> return ()
(_, Left _) -> return () -- ???
(_, Right x) -> --do things, render, recurse into next frame

这种模式有两件事让我感到不舒服。首先,我将 Right undefined 传递给对 stepWire 的两个调用。我认为(如果我的理解是正确的)此参数用于将事件发送到线路,并且由于我的线路不使用任何事件,因此这是“安全的”,但感觉很糟糕( EDIT 可能是“事件” "在这里是错误的词——本教程将其描述为“阻塞值”,但重点仍然存在——我从不打算阻塞并且不在我的线路中的任何地方使用 e 参数)。我查看了是否有一个 stepWire 版本,适用于您知道从未发生过事件并且即使您确实有事件但看不到事件也不会响应的情况。我尝试将 e 参数设为 () ,然后在任何地方传递 Right () ,这感觉比 undefined 稍微不那么脏,但似乎仍然不能完全代表我的意图。

同样,返回值也是一个 Either 。这对于 shouldQuit 线来说是完美的,但请注意,我必须在 wt' 上进行模式匹配,即 spin 线的输出。我真的不知道抑制它意味着什么,所以我只是 return () ,但我可以想象这会随着电线数量的增加而变得笨拙,而且,它似乎并不能代表我的意图——有一根永远不会抑制的电线,我可以一直依靠它来保存下一个值。

因此,尽管我有可以运行的代码,但我还是有一种不安的感觉,即我不知何故“做错了”,而且由于 Netwire 5 是相当新的,因此很难找到我可以检查和检查的“惯用”代码示例。看看我是否接近标记。这是图书馆的用途还是我遗漏了什么?

编辑 :通过将 Eitherspin 组合成一个 spin ,我设法解决了我提到的第二个问题( shouldQuitWire 结果上的模式匹配):
shouldContinuePlaying :: (Monoid e, Functor m, Monad m) => Wire s e (GLFWInputT m) a a
shouldContinuePlaying = keyNotPressed GLFW.Key'Escape

game :: (HasTime t s, Monoid e, Functor m, Monad m) => Wire s e (GLFWInputT m) a GL.GLfloat
game = spin . shouldContinuePlaying

步进这条线给了我一个更合理的返回值——如果是 Left 我可以退出,否则我有数据可以处理。它还暗示了比我原来的方法更大程度的可组合性。

尽管如此,我仍然必须将 Right undefined 作为输入传递给这条新线路。诚然,现在只有其中一种,但我仍然不确定这是否是正确的方法。

最佳答案

在程序的最顶层,您将拥有一些(缩写)类型 Wire a b 的电线。 .这将需要传递 a 类型的东西它会返回 b 类型的东西每走一步。例如,两个 ab可以一些WorldState用于游戏或可能 [RigidBody]对于物理模拟器。在我看来,可以通过Right undefined在顶层。

话虽如此,您忽略了重要的 Alternative Wire a b 的实例对于输入线。它提供了一个运算符 <|>它以一种非常好的方式工作:

假设我们有两条线:

w1 :: Wire a b
w2 :: Wire a b

如果 w1 抑制,则
w1 <|> w2 == w2

如果 w1 不抑制,则
w1 <|> w2 == w1

这意味着 w1 <|> w2只有在 w1 时才会禁止和 w2抑制。这很棒,这意味着我们可以做以下事情:
spin :: (HasTime t s, Monad m) => Wire s e m a GL.GLfloat
spin = integral 0 . (10 . keyPressed GLFW.Key'F <|> 5)

当您按 F ,你会以两倍的速度旋转!

如果你想在按下按钮后改变连线的语义,你必须更有创意,但不要太多。如果您的电线表现不同,则意味着您正在进行某种转换。 documentation for switches大多需要你 follow the types .

这是一条线,在您按下给定的键之前,它的作用类似于身份线,然后将永远禁止:
trigger :: GLFW.Key -> GameWire a a
trigger key =
rSwitch mkId . (mkId &&& ((now . pure mkEmpty . keyPressed key) <|> never))

有了这个,你可以做很酷的事情,比如:
spin :: (HasTime t s, Monad m) => Wire s e m a GL.GLfloat
spin = integral 0 . spinSpeed
where
spinSpeed = 5 . trigger GLFW.Key'F -->
-5 . trigger GLFW.Key'F -->
spinSpeed

每当您点击 F 时,这将在前进和后退之间切换微调器。 .

关于haskell - 正确使用网线(五),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28595783/

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