Функция, запрашивающая ввод в haskell (с печатью в блоке do) - PullRequest
0 голосов
/ 25 апреля 2020

я действительно новичок в haskell, и я пытаюсь реализовать простую игру connect4, когда я пытаюсь заставить игрока ввести новый ход, я хочу предложить ему сделать это. Вот соответствующий код, который у меня есть:

advanceHuman :: Board -> Board
advanceHuman b = do
     let column = query 
     if (snd((possibleMoves b)!!(column-1)) == cha)
         then updateBoard b p1 column
         else advanceHuman b

query :: Int
query = do {
    print ("escoge una columna vacia") ; -- choose empty column
    input <- getLine ;
    return (read input) }

. Как вы видите, я пытаюсь подсказать игроку, получить его ответ и передать его другим функциям (предполагается, что игрок будет сотрудничать и введет действительный код). число). Тем не менее, это сообщение об ошибке, которое я получаю, когда пытаюсь скомпилировать

    * Couldn't match expected type `Int' with actual type `IO b0'
    * In a stmt of a 'do' block: print ("escoge una columna vacia")
      In the expression:
        do print ("escoge una columna vacia")
           input <- getLine
           return (read input)
      In an equation for `query':
          query
            = do print ("escoge una columna vacia")
                 input <- getLine
                 return (read input)
   |
47 |     print ("escoge una columna vacia") ;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Насколько я понимаю, я должен иметь возможность печатать строку в блоке do независимо от того, что эта функция выводит правильно? В чем проблема, я неправильно понял, как работает "do"?

UPDT

в соответствии с запросом, это другие задействованные функции

possibleMoves:: Board -> Board 
possibleMoves b = take sx b

updateBoard:: Board -> String -> Int -> Board
updateBoard b pl col = b

Обновление еще не было реализовано так что у него просто есть какой-то фиктивный код, чтобы успокоить компилятор '= пусто), проверьте верхнюю часть столбца, который игрок указывает с помощью «запроса», чтобы увидеть, является ли это законным ходом; если нет, процесс начинается снова.

Ответы [ 2 ]

3 голосов
/ 25 апреля 2020

Теория монад очень интересна, и вам стоит окунуться в нее и понять все это глубоко. Но вот в основном неправильный ответ, чтобы помочь вам начать кодирование, не беспокоясь обо всем этом.

Существует два вида вещей: значения и действия . Действия имеют типы, заключенные в IO, например IO Int, IO String, IO (Maybe [Bool]). Действия - это способ ввода / вывода. Они выполняют некоторые операции ввода-вывода и после завершения возвращают значение типа, который они переносят.

Действия создаются с do, и обычно последняя строка имеет return <value> (или является другой действие, в этом случае он использует возвращаемое значение этого). Итак, ваше query - это действие:

query :: IO Int   -- notice the IO
query = do
    print ("escoge una columna vacia")
    input <- getLine
    return (read input)

То, как вы используете действия, связывает их , используя <-, что вы уже сделали с getLine. Это можно сделать только в блоке do. Поэтому, если вы хотите использовать query в advanceHuman, вам необходимо связать его:

advanceHuman b = do 
    input <- query
    ...

Имя (или шаблон) слева от привязки становится значением типа что бы ни было завернуто в IO - в данном случае Int.

Однако я сказал, что do создает действия. Это означает, что advanceHuman должен возвращать тип действия:

advanceHuman :: Board -> IO Board
advanceHuman b = do 
    input <- query
    ...

Единственными вещами, которые могут быть строками в блоке do, являются действия, либо привязанные к значениям, либо нет, и return <value> (что, как выясняется, это тоже действие).

Вы должны связать действия, прежде чем использовать их значения. Например, если у вас есть

getX :: IO Int
getY :: IO Int

Тогда вы не можете сказать getX + getY, чтобы получить их сумму. Вы должны сказать do { x <- getX; y <- getY; return (x + y) } (или liftA2 (+) getX getY, но давайте не будем забегать вперед).

Если вы хотите связать имя со значением вместо действия, вы используйте вместо этого let. Так что в advanceHuman вы использовали let, когда вы должны были использовать <-, потому что query - это действие.

Надеюсь, это поможет.

1 голос
/ 25 апреля 2020

Haskell не использует фигурные скобки для кода области. Вместо этого это сделано с отступом. return не похож на ваш типичный оператор return, который вы найдете в императивном программировании. Что он на самом деле делает, так это оборачивает ваше значение в тип монады. Вы можете проверить это в GHCI:

Prelude> :t return
return :: Monad m => a -> m a

нотация do является способом составления монадических c действий. Трудно объяснить, что это значит без теории, которую я не расскажу здесь. (Я предлагаю вам взять книгу haskell). Это просто синтаксис Sugar, и вы go обходитесь без него (я покажу ниже).

Чтобы исправить ваш код хотя бы:

query :: (IO Int) 
query = do 
    print ("escoge una columna vacia")
    input <- getLine
    return (read input)

IO - это тип, который становится типом когда Int применяется к нему. Тип (IO Int)

Вот код без do:

module Examples where
query :: (IO Int) 
query = 
    print ("escoge una columna vacia") >>
    getLine >>= (\x ->
    return (read x))

Я предлагаю вам посмотреть, что >> и >> = делают. В общем, вам следует избегать использования функции read, это небезопасная функция (что, если вы там набрали строку вместо int?) Существуют другие более безопасные функции, которые не предполагают, что у вас реализован класс типов

...