Haskell 与哈斯凯尔州立大学打交道
因此,我尝试使用状态实现一个Haskell游戏,作为游戏的一部分,我想实现保存当前玩家的名字并在调用时检索它的方法。我有两个助手函数popStack和pushStack,它们分别将值弹出和推入堆栈 当前代码:Haskell 与哈斯凯尔州立大学打交道,haskell,functional-programming,state,Haskell,Functional Programming,State,因此,我尝试使用状态实现一个Haskell游戏,作为游戏的一部分,我想实现保存当前玩家的名字并在调用时检索它的方法。我有两个助手函数popStack和pushStack,它们分别将值弹出和推入堆栈 当前代码: import Control.Monad.State data Gamestate = Gamestate { gamestack :: [String], gamememory :: String } type NewGameState = State G
import Control.Monad.State
data Gamestate = Gamestate {
gamestack :: [String],
gamememory :: String
}
type NewGameState = State GameState
popStack :: NewGameState String
popStack = state $ \st -> case gamestack st of
[] -> (0.0,st)
x:xs -> (x,st { gamestack = xs })
pushStack :: String -> NewGameState ()
push d = modify $ \st -> st { gamestack = d : gamestack st }
我为saveName和getName编写了以下代码
saveName :: NewGameState ()
saveName = do
memory <-head
pushStack $ x
getName :: NewGameState ()
getName = do
memory <- head gamestack
popStack $ memory
saveName::NewGameState()
saveName=do
内存如果某个东西位于=pushStack的右侧。游戏记忆
如果你想成为一名花花公子
popStack
不接受任何参数,因此我不确定您想要什么。我最好的猜测是,它应该只抓取我们推送的姓氏,如果某个东西在=pushStack的右侧,它就是对popStack的调用。游戏记忆
如果你想成为一名花花公子
popStack
不接受任何参数,因此我不确定您想要什么。我的最佳猜测是,它应该只抓取我们推的姓氏,这只是对popStack
的一个调用。我将通过向您展示执行您试图执行的操作的惯用方法来回答您的问题。我将指出我在你的代码中修正了什么
第一个问题:游戏状态的大小写不一致。在Haskell中,大写很重要,所以我将所有内容重命名为GameState
所以在做了修复之后,我做的第一件事就是为两个数据类型的字段定义镜头。这使得修改状态子集的有状态操作变得更加容易。当我谈到其余函数的实现时,您将看到这一点:
import Control.Monad.State
import Control.Lens
data GameState = GameState
{ _gamestack :: [String]
, _gamememory :: String
}
gamestack :: Lens' GameState [String]
gamestack k (GameState s m) = fmap (\s' -> GameState s' m) (k s)
gamememory :: Lens' GameState String
gamememory k (GameState s m) = fmap (\m' -> GameState s m') (k m)
type NewGameState = State GameState
请注意,您不必像这样手动定义镜头。除了定义gamememory
和gamestack
,您还可以这样做:
{-# LANGUAGE TemplateHaskell #-} -- Note the extension
import Control.Lens
data GameState = GameState
{ _gamestack :: [String]
, _gamememory :: String
}
makeLenses ''GameState
无论您选择哪种方式,一旦我们有了这些镜头,我们就可以编写push
和pop
,只要是一个列表,他们就不关心自己的行为状态:
pop :: State [a] (Maybe a)
pop = do
s <- get
case s of
[] -> return Nothing
x:xs -> do
put xs
return (Just x)
push :: a -> State [a] ()
push d = modify (d:)
请注意,我是如何使用zoom
来本地化push
和pop
来操作gamememory
或gamestack
字段的<代码>缩放
将镜头移到子字段,然后运行有状态操作,就好像整个状态就是该子字段一样。这很酷,因为现在push
和pop
的可重用性更高,我们不必在其中加入特定的状态数据类型选择
这还使用=
,设置给定字段。这基本上与:
lens .= x = zoom lens (put x)
要了解更多有关镜头、(.=)
和变焦的信息,您可能需要阅读我写的内容
编辑:根据要求,以下是无镜头版本:
import Control.Monad.State
data GameState = GameState
{ gamestack :: [String]
, gamememory :: String
}
type NewGameState = State GameState
saveName :: NewGameState ()
saveName = do
GameState stack memory <- get
put (GameState (memory:stack) memory)
getName :: NewGameState ()
getName = do
GameState stack memory <- get
case stack of
[] -> put (GameState stack memory)
x:xs -> put (GameState xs x )
import Control.Monad.State
数据游戏状态=游戏状态
{游戏堆栈::[字符串]
,gamememory::String
}
键入NewGameState=状态GameState
saveName::NewGameState()
saveName=do
游戏状态堆栈内存放置(游戏状态xs x)
我将通过向您展示做您想做的事情的惯用方法来回答您的问题。我将指出我在你的代码中修正了什么
第一个问题:游戏状态的大小写不一致。在Haskell中,大写很重要,所以我将所有内容重命名为GameState
所以在做了修复之后,我做的第一件事就是为两个数据类型的字段定义镜头。这使得修改状态子集的有状态操作变得更加容易。当我谈到其余函数的实现时,您将看到这一点:
import Control.Monad.State
import Control.Lens
data GameState = GameState
{ _gamestack :: [String]
, _gamememory :: String
}
gamestack :: Lens' GameState [String]
gamestack k (GameState s m) = fmap (\s' -> GameState s' m) (k s)
gamememory :: Lens' GameState String
gamememory k (GameState s m) = fmap (\m' -> GameState s m') (k m)
type NewGameState = State GameState
请注意,您不必像这样手动定义镜头。除了定义gamememory
和gamestack
,您还可以这样做:
{-# LANGUAGE TemplateHaskell #-} -- Note the extension
import Control.Lens
data GameState = GameState
{ _gamestack :: [String]
, _gamememory :: String
}
makeLenses ''GameState
无论您选择哪种方式,一旦我们有了这些镜头,我们就可以编写push
和pop
,只要是一个列表,他们就不关心自己的行为状态:
pop :: State [a] (Maybe a)
pop = do
s <- get
case s of
[] -> return Nothing
x:xs -> do
put xs
return (Just x)
push :: a -> State [a] ()
push d = modify (d:)
请注意,我是如何使用zoom
来本地化push
和pop
来操作gamememory
或gamestack
字段的<代码>缩放
将镜头移到子字段,然后运行有状态操作,就好像整个状态就是该子字段一样。这很酷,因为现在push
和pop
的可重用性更高,我们不必在其中加入特定的状态数据类型选择
这还使用=
,设置给定字段。这基本上与:
lens .= x = zoom lens (put x)
要了解更多有关镜头、(.=)
和变焦的信息,您可能需要阅读我写的内容
编辑:根据要求,以下是无镜头版本:
import Control.Monad.State
data GameState = GameState
{ gamestack :: [String]
, gamememory :: String
}
type NewGameState = State GameState
saveName :: NewGameState ()
saveName = do
GameState stack memory <- get
put (GameState (memory:stack) memory)
getName :: NewGameState ()
getName = do
GameState stack memory <- get
case stack of
[] -> put (GameState stack memory)
x:xs -> put (GameState xs x )
import Control.Monad.State
数据游戏状态=游戏状态
{游戏堆栈::[字符串]
,gamememory::String
}
键入NewGameState=状态GameState
saveName::NewGameState()
saveName=do
游戏状态堆栈内存放置(游戏状态xs x)
新游戏状态
是一个糟糕的名字——它根本不是一个新的游戏状态,它是一个携带状态的单子。我刚把它叫做游戏
pushStack
vspush
-您给出了名为pushStack
的签名,然后是名为push
的函数。挑一个
在popStack
中,您有[]->(0.0,st)
让我们面对现实,0.0
不是字符串,为什么要尝试返回它?当弹出一个空堆栈时,您是否不知道该怎么做?你用“
代替怎么样
saveName和getName你甚至还没有说你想让它们做什么。您似乎接受了其他回答者的解释,因此我们可以使用记录更新语法
最后,这里是一些代码