我正在尝试实现我可以在棋盘上移动不同块的游戏。我已经设置了移动的条件和块的实际移动,现在我试图添加的是用户输入的递归循环,这将理想地解决游戏。
这就是我写的:
userInput tableArr = do
str <- getLine
if str == "help"
then printHelp tableArr
else if str == "gg"
then putStrLn "Game over, you lose"
else if length str == 3
then isCorrectInput str tableArr
else if str == "table"
then let printer = printTable tableArr in userInput tableArr
else displayErrorMsg tableArr
isCorrectInput :: String -> [[Char]] -> [[Char]]
isCorrectInput str tableArr = do
let val = filter (not . isSpace) str
let move = val !! 1
let number = digitToInt(val !! 0)
let posArr = findBlock (myElem number) tableArr
if length val == 2
then (if move == 'R'
then let newArr = moveRIGHT tableArr number posArr in userInput newArr
else if move == 'L'
then let newArr = moveLEFT tableArr number posArr in userInput newArr
else if move == 'U'
then let newArr = moveUP tableArr number posArr in userInput newArr
else if move == 'D'
then let newArr = moveDOWN tableArr number posArr in userInput newArr
else displayErrorMsg tableArr)
else displayErrorMsg tableArr
displayErrorMsg tableArr = do
putStr "Wrong input, type again: "
userInput tableArr
现在对这里发生的事情进行一点澄清。
函数userInput
接受[[Char]]
(我没有设置签名原因,我对这件事的返回类型感到困惑)。现在用户输入有几个条件,但错误在isCorrectInput
中抛出。 (也是let printer = printTable tableArr in userInput tableArr
写的正确吗?我不想添加递归结束函数printTable,它基本上只包含putStrLn
来打印我的游戏板。)
现在让我们快速浏览isCorrectInput
。这样可以更准确地评估用户输入abit,并根据用户输入进行操作(通常用户输入的方式为“4 R”,这意味着基本上是4号到右侧的块。无论如何,如果其中一个移动条件匹配则调用move函数,将其添加到新变量中,然后再次递归调用函数userInput
。
函数displayErrorMsg
只是为了再次显示消息和调出递归。
现在我得到的错误是:
Couldn't match expected type `IO ()' with actual type `[[Char]]'
In the expression: isCorrectInput str tableArr
In the expression:
if length str == 3 then
isCorrectInput str tableArr
else
if str == "table" then
let printer = ... in userInput tableArr
else
displayErrorMsg tableArr
In the expression:
if str == "gg" then
putStrLn "Game over, you lose"
else
if length str == 3 then
isCorrectInput str tableArr
else
if str == "table" then
let ... in userInput tableArr
else
displayErrorMsg tableArr
我希望我的解释很清楚,因为我不是母语为英语的人,你们中的一个人可以证明我的代码有什么问题。
谢谢:)
答案 0 :(得分:3)
你的代码很乱 - 不止一个级别,所以当我试图整理它并帮助你理解这个错误并且最重要的是在将来避免它时,请耐心等待。
TL,DR为您声明类型签名中isCorrectInput :: String -> [[Char]] -> [[Char]]
但丰富ifs末尾的所有函数都是IO ()
类型,因此签名应该是isCorrectInput :: String -> [[Char]] -> IO ()
(因为这里有一些没有实现的功能。)
但首先要尝试使用以下原则
IO ()
并坚持使用&#34; pure&#34;功能if
s 如果您无法找出代码片段的签名,请使用ghci
Prelude> :type functionnamewhosesignatureidontknow
会帮助你
现在到您的代码 - 我将更新此部分,因为我得到更多信息
我尝试从一个带有类型签名而没有实现的函数框架开始
Main.hs
type GameBoard = [String]
data Direction = L|R|U|D deriving (Eq,Show)
type Move = (Int,Direction)
mainLoop :: IO ()
-- checks for abort condition and gives helpful information,
-- while playing the game
mainLoop = undefined
parseMove :: GameBoard -> String -> Maybe Move
-- checks if a given (pure) input string is a valid move for
-- a given game state, for now I use Maybe to indicate all versions of invalid
-- input
parseMove = undefined
help :: IO ()
-- prints the rules of the game
help = undefined
printErr :: String -> IO ()
printErr = undefined
现在这应该编译 - 让我们填满mainLoop
- 哦,我注意到我们想要使用当前的GameBoard in that one so add it as a parameter - note that there are techniques to make this a bit more convenient (notably the so called
状态`monad)
mainLoop :: GameBoard -> IO ()
mainLoop gb = do
str <- getLine
case str of "help" -> do help
mainLoop gb
"gg" -> putStrLn "Game Over"
-- "table" I omit for sake of shortness
_ -> do let nextMove = parseMove gb str
mainLoop (nextBoard gb nextMove)
nextBoard :: GameBoard -> Move -> GameBoard
nextBoard = undefined -- try to implement this one on your own
哎呀我的代码中出现了错误 - 但是嘿编译器告诉我们nextMove是Maybe Move
nextBoard
期望Move
如此整齐,以便快速上升
_ -> case parseMove gb str
of Just nextMove -> mainLoop (nextBoard gb nextMove)
_ -> do putStrLn "incorrect move - please repeat"
mainLoop gb
下一个挑战是整理解析输入,但首先我重新定义了我的GameBoard
,知道haskell是一种静态类型语言,我想利用它并实际使用它对我有利
type Tile = Empty | Movable | Fixed | Player deriving (Eq)
instance Show Tile where
-- I usally use my favourite unicode box characters for games like this one
show Empty = " "
show Movable = "0"
show Fixed = "#"
show Player = "@"
type GameBoard = [[Tile]]
首先,我会将String
拆分为&#39; &#39;但是,如果我不知道该功能的名称怎么办?
=&GT;使用HOOGLE https://www.haskell.org/hoogle/?hoogle=%28a+-%3E+Bool%29+-%3E+%5Ba%5D+-%3E+%28%5Ba%5D%2C%5Ba%5D%29
并在Prelude
中为两位候选人使用ghci,我们将break
作为所需的
parseMove :: GameBoard -> String -> Maybe Move
-- note despite us changing what Gameboard is this signature has not changed!
-- first of all I assume that number and dir are separated by a space
-- watch out there is still a space at _dir
-- and both tuple entries are still Strings
parseMove gb = do let (_n, _dir) = break (== ' ') str
n <- parseInt n
dir <- parseDir _dir
if canPlayerMove gb (n,dir) then return (n,dir)
else Nothing
where parseInt :: String -> Maybe Int
parseInt s = if all isDigit s -- you might import or define `isDigit` yourself
then Just (read s)
else Nothing
parseDir :: String -> Maybe Direction
parseDir "L" = Just L
parseDir "R" = Just R
-- ..
parseDir _ = Nothing
canPlayerMove :: GameBoard -> Move -> Bool
canPlayerMove = undefined -- here you take care that the Game logic is respected
我希望你能得到一些如何整理代码的图片。 另请注意,此代码是迄今为止最好的代码,我希望我可以理解地编写它
顺便说一下。使用do
时,<-
return
.. parseMove
Maybe
是一个巧妙的伎俩,右箭头需要Maybe value
如果它是value
,则右侧并拉出Just x
,否则将丢弃该点下方的整个计算并返回Nothing
。
答案 1 :(得分:2)
您的问题是函数isCorrectInput
不在IO
monad中。看看它的类型:
isCorrectInput :: String -> [[Char]] -> [[Char]]
和错误消息
Couldn't match expected type `IO ()' with actual type `[[Char]]'
In the expression: isCorrectInput str tableArr
由于你是monadic(由于使用了do
),因此假定它应该返回monadic结果。由于它似乎使用IO
monad,你应该将其类型签名更正为(注意String = [Char]):
isCorrectInput :: String -> [String] -> IO [String]
并且在函数中表示结果的每个表达式应该是monadic表达式或使用return
以将结果放入给定的monad中。因此,要修复此错误,只需添加return
:
if length str == 3 then
return (isCorrectInput str tableArr)
else ...
我的建议是先了解Haskell IO和Monads。 Learn you a Haskell的第9章和第12章提供了必要的背景知识。