键入错误评估用户输入:IO()与实际类型[[Char]]。哈斯克尔

时间:2016-01-05 18:34:55

标签: haskell

我正在尝试实现我可以在棋盘上移动不同块的游戏。我已经设置了移动的条件和块的实际移动,现在我试图添加的是用户输入的递归循环,这将理想地解决游戏。

这就是我写的:

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

我希望我的解释很清楚,因为我不是母语为英语的人,你们中的一个人可以证明我的代码有什么问题。

谢谢:)

2 个答案:

答案 0 :(得分:3)

你的代码很乱 - 不止一个级别,所以当我试图整理它并帮助你理解这个错误并且最重要的是在将来避免它时,请耐心等待。

TL,DR为您声明类型签名中isCorrectInput :: String -> [[Char]] -> [[Char]]但丰富ifs末尾的所有函数都是IO ()类型,因此签名应该是isCorrectInput :: String -> [[Char]] -> IO ()(因为这里有一些没有实现的功能。)

但首先要尝试使用以下原则

  • 尽可能避免IO ()并坚持使用&#34; pure&#34;功能
  • 使用guards / case语句而不是嵌套的if s
  • 除非你有充分的理由 - 代码&#34;行走&#34;在屏幕的右侧 - 是一个不好的迹象
  • 编写小而易懂的功能
  • 如果您无法找出代码片段的签名,请使用ghci

    Prelude> :type functionnamewhosesignatureidontknow
    

    会帮助你

  • 阅读&#34;了解你的好消息&#34;或者此stackoverflow标记的信息部分中链接的一些其他文献
  • 如果您附近有一个haskell /函数式编程用户组,请加入

现在到您的代码 - 我将更新此部分,因为我得到更多信息

我尝试从一个带有类型签名而没有实现的函数框架开始

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章提供了必要的背景知识。