Страж внутри блока «до» - haskell - PullRequest
3 голосов
/ 17 января 2020

Я хочу написать простую игру «Угадай число» - с n попытками. Я хочу добавить некоторые условия и хиты. Можно ли использовать охранники внутри do блока?

Вот мой код:

game = return()
game n = do putStrLn "guess number: 0-99"
            number<-getLine
            let y = read number
            let x =20
            | y>x = putStrLn "your number is greater than x"
            | y<x = putStrLn "your number is less than x"
            | y==x  putStrLn "U win!!"
            | otherwise = game (n-1)

уже получил ошибку

error: parse error on input ‘|’

Это можно исправить с помощью некоторого пробела или просто невозможно сделать?

Ответы [ 3 ]

6 голосов
/ 17 января 2020

Там много проблем.

Во-первых, вы не можете сказать game = что-то и game n = что-то, поэтому удалите строку game = return (). (Возможно, вы пытались написать сигнатуру типа, но это не так.)

Во-вторых, вы не можете использовать синтаксис защиты в произвольных местах. Наиболее близким к тому, что вы написали, являются многофакторные if-выражения , которые позволили бы вам написать следующее:

{-# LANGUAGE MultiWayIf #-}
game n = do putStrLn "guess number: 0-99"
            number<-getLine
            let y = read number
            let x =20
            if
              | y>x -> putStrLn "your number is greater than x"
              | y<x -> putStrLn "your number is less than x"
              | y==x-> putStrLn "U win!!"
              | otherwise -> game (n-1)

В-третьих, класс типов Ord должен быть предназначен для типы с полным заказом, поэтому, если вы не используете незаконные вещи, такие как NaN, у вас всегда будет один из y>x, y<x или y==x, поэтому otherwise никогда не будет введен.

В-четвертых, сравнение с <, == и > является унидиоматическим c и медленным, поскольку оно должно повторять сравнение. Вместо этого сделайте что-то вроде этого:

case y `compare` x of
  GT -> _
  LT -> _
  EQ -> _
5 голосов
/ 17 января 2020

A do выражение [Haskell -report] состоит только из операторов exp, pat < exp и let &hellip;, и их компилятор отменит их. Следовательно, без некоторых языковых расширений вы не можете писать охранники в блоке do. Кроме того, в любом случае, это не очень хорошая идея. Что если вы, например, захотите использовать два «защитных блока» рядом друг с другом? Тогда эти два «слились бы», и, таким образом, охранники первого блока уже уничтожили бы (почти) все дела.

Вы можете использовать другое предложение let здесь:

game :: IO ()
game 0 = return ()
game n = do
    putStrLn "guess number: 0-99"
    number <- getLine
    let y = read number
    let x = 20
    <b>let action</b> | y > x = putStrLn "your number is greater than x" >> game (n-1)
               | y < x = putStrLn "your number is less than x" >> game (n-1)
               | otherwise = putStrLn "U win!!"
    <b>action</b>

Примечание что otherwise в исходном вопросе никогда не сработает, поскольку значение меньше, больше или равно другому значению.

3 голосов
/ 18 января 2020

Вы также можете просто использовать case или LambdaCase.

{-# LANGUAGE LambdaCase #-}

game  :: Int -> IO ()
game n = case n of
  0 -> putStrLn "used all attempts"
  n -> 
    putStrLn "guess a number: 0 - 99" >>
    (`compare` 20) . read <$> getLine >>= 
      \case 
        EQ -> putStrLn "U win"
        GT -> putStrLn "your number is greater than x" >> 
              game (n - 1)
        LT -> putStrLn "your number is less than x" >> 
              game (n - 1)
...