如何解决将打印文本的函数作为参数传递,在 Haskell 中的新状态之后执行它
https://github.com/agutie58/landOfLispInHaskell/blob/main/exampleLoLTextGameHaskell.hs
EDIT2:实际游戏的一个例子。]
[原始问题]
我有一个基于 run
的函数 Domain
和一个事件列表:
- 如果处理了所有事件,它会侦听新事件并
- 如果它们是事件,它会处理其中的几个。
大多数事件都会修改 Domain
:
data Domain = Domain (String,World) deriving (Show)
data World = World {loc :: String,descSites :: [(Key,String)],mapSites :: [(Key,[Lloc])],objects:: [Key],siteObjects::[(Key,String)]} deriving (Show)
run :: Domain -> [Event] -> IO ()
run dm [] = do
events <- uiUpdate dm
run dm events
run _ (EventExit:_) =
return ()
run dm (e:es) =
run (dmUpdate dm e) es
我想要关注的部分是 run (dmUpdate dm e) es
,其中 dmUpdate dm e
返回一个 Domain
值:
dmUpdate :: Domain -> Event -> Domain
dmUpdate (Domain v) (EventLook) = look (snd v)
dmUpdate (Domain v) (EventWalk direction) = walk direction (snd v)
dmUpdate dm _ _ = dm
哪里:
look :: World -> Domain
walk :: String -> World -> Domain
-- etc.
我想呈现(打印到控制台)新状态的结果。例如:
dmUpdate (Domain v) (EventLook) = do let newDomain = look (snd v)
putStr (fst newDomain)
newDomain
但它不起作用。我尝试计算世界的新状态,然后执行 I/O,然后尝试将 newDomain
作为参数返回。
我想传递这样的函数:
run dm (e:es) =
run (dmUpdate dm e renderMsg) es
where renderMsg txt = (putStr txt) >> (hFlush stdout)
...为了做类似的事情:
-- dmUpdate :: dmUpdate :: Domain -> Event -> (String -> IO ()) -> IO () -> Domain
dmUpdate (Domain v) (EventLook) (renderMsg) = let newDomain = look (snd v)
renderMsg (fst newDomain)
但不起作用。
任何想法!?提前致谢!
[EDIT1]
我也试过:
dmUpdate :: Domain -> Event -> IO Domain
dmUpdate (Domain v) (EventLook) = do let newDomain = look (snd v)
putStr (fst newDomain)
newDomain
dmUpdate dm _ _ = () dm
...但我收到了这条消息:
[2 of 2] Compiling Main ( textGameMain.hs,interpreted )
textGameMain.hs:25:1: error:
Equations for ‘dmUpdate’ have different numbers of arguments
textGameMain.hs:(25,1)-(27,47)
textGameMain.hs:33:1-23
|
25 | dmUpdate (Domain v) (EventLook) = do let newDomain = look (snd v)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...
textGameMain.hs:70:8: error:
• Couldn't match expected type ‘Domain’
with actual type ‘IO Domain’
• In the first argument of ‘run’,namely ‘(dmUpdate dm e)’
In the expression: run (dmUpdate dm e) es
In an equation for ‘run’: run dm (e : es) = run (dmUpdate dm e) es
|
70 | run (dmUpdate dm e ) es
然后,我尝试更改run
:
run dm (e:es) =
run () (dmUpdate dm e ) es
但我没有设法让它工作...... :S
[EDIT2]
所以,我的代码是这样做的(它基于 Land of Lisp,并且是对 Haskell 的改编):
*Main> main WORLD> look you are in the living-room. a wizard is snoring loudly on the couch. There is door going west from here. There is ladder going upstairs from here. You see a whiskey on the floor. You see a bucket on the floor. WORLD> walk west WORLD> look you are in a beautiful garden. there is a well in front of you. There is door going east from here. You see a frog on the floor. You see a chain on the floor.
[EDIT3]
(完整代码 - 实际上,为了简单起见,我将两个文件合并为一个文件……可以通过其他方式添加文件吗!?)
https://github.com/agutie58/landOfLispInHaskell/blob/main/exampleLoLTextGameHaskell.hs
ghci exampleLoLTextGameHaskell.hs
main
(...然后,要编辑 2 的东西...)
解决方法
您的代码远未完成,包含很多 - 对于您的问题 - 不必要的部分。我复制了你的代码,简化了一点,并做了一些猜测工作。这是我的“固定”版本。
module Tmp where
import Text.Read (readMaybe)
{-| Be clear about your types. Introducing type alisases helps others read your code.
I simply guessed that the string-part of the Domain is the UI state -}
type UIState = String
data Domain = Domain (UIState,World) deriving Show
data World = World {loc:: Int} deriving Show
-- | I Have only implemented two directions,so that this example is easy to work with.
data Dir = L | R deriving (Read,Show)
{- | These were the event types that you had in your code. I elaborated them a little.
By using the "deriving (Read)" we get a low-code input mechanism,but you should probalbly write
your own input parser
-}
data Event = EventExit | EventWalk Dir| EventLook deriving (Read)
-- | The run-loop now has TWO steps. Render UI and get new events. Process events. Finally recurse.
run :: Domain -> [Event] -> IO ()
run dm [] = uiUpdate dm >> getAction >>= run dm
run _ (EventExit:_) = return ()
run dm (e:es) = run (dmUpdate dm e) es
{-| Update the domain when a single event acts on it -}
dmUpdate :: Domain -> Event -> Domain
dmUpdate (Domain v) (EventLook) = look (snd v)
dmUpdate (Domain v) (EventWalk direction) = walk direction (snd v)
dmUpdate dm _ = dm
look w = Domain ("You are at coordinate " ++ (show .loc $ w),w)
walk L w@World{loc=l}= Domain ("You went left",w{loc=l-1})
walk R w@World{loc=l} = Domain ("You went right",w{loc=l+1})
{-| Present the "output" that the domain holds for us -}
uiUpdate :: Domain -> IO ()
uiUpdate dm = do
let Domain (usState,s) = dm
putStrLn usState
{-| Ask user for input. Only a single event is collected. -}
getAction :: IO [Event]
getAction = do
putStrLn "What do you want to do? Choose between EventExit | EventWalk R | EventWalk L | EventLook"
act <- readMaybe <$> getLine
case act of
Nothing -> putStrLn "Not a valid action" >> getAction
Just evt -> pure [evt]
main :: IO ()
main = run (Domain ("",World 0)) [EventLook]
最后,您可能想要查看 StateT,以便您可以抽象出一直传递的 Domain 对象。但这超出了这个问题的范围,我想。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。