我正在逐步介绍Haskell上的一些入门资料,并尝试为命令行完成此愚蠢的Rock,Paper和Scissors实现。
我认为输入的类型防护足以使编译器确信输入为RPS
类型,但可惜不是。
如何告诉编译器输入数据是一种类型还是另一种类型?
data RPS = Rock | Paper | Scissors
_shoot :: RPS -> RPS -> String
_shoot Rock Paper = "Paper beats rock, you win!"
_shoot Paper Rock = "Paper beats rock, you loose."
_shoot Rock Scissors = "Rock beats scissors, you loose."
_shoot Scissors Rock = "Rock beats scissors, you win!"
_shoot Paper Scissors = "Scissors beats paper, you win!"
_shoot Scissors Paper = "Scissors beats paper, you loose!"
_shoot Rock Rock = "Tie!"
_shoot Scissors Scissors = "Tie!"
_shoot Paper Paper = "Tie!"
isRPS :: String -> Bool
isRPS s = elem s ["Rock", "Paper", "Scissors"]
main :: IO ()
main = do
putStrLn "Rock, Paper, or Scissors?"
choice <- getLine
if isRPS choice -- this was my idea but is apparently not good enough
then putStrLn (_shoot choice Rock)
-- ^^^^^^
-- Couldn't match type ‘[Char]’ with ‘RPS’ Expected type: RPS Actual type: String
else putStrLn "Invalid choice."
答案 0 :(得分:11)
您没有将choice
(是String
)转换为RPS
,甚至没有将Maybe RPS
转换为:
readRPS :: String -> Maybe RPS
readRPS "rock" = Just Rock
readRPS "paper" = Just Paper
readRPS "scissors" = Just Scissors
readRPS _ = Nothing
因此,在给定输入有效的情况下,我们在此返回Just x
(与x
对应的RPS
项),或者如果字符串不是有效的选项,则返回Nothing
。
然后我们可以将其实现为:
import Data.Char(toLower)
main :: IO ()
main = do
putStrLn "Rock, Paper, or Scissors?"
choice <- getLine
case readRPS (map toLower choice) of
Just rps -> putStrLn (_shoot rps Rock)
Nothing -> putStrLn "Invalid choice."
main
答案 1 :(得分:5)
您快到了,只需使用read函数即可将用户的字符串转换为RPS
数据类型。
您需要做的第一件事是使RPS
是Read
类型类的实例。通过将data
声明修改为:
data RPS = Rock | Paper | Scissors deriving Read
deriving Read
所做的就是为RPS
提供Read
类型类的默认实例,该实例的工作方式很明显:read "Rock"
将变成Rock
,依此类推,前提是编译器知道您正在使用read
类型值的上下文中使用RPS
。
然后,您需要在main
函数中进行的所有更改:
putStrLn (_shoot choice Rock)
到
putStrLn (_shoot (read choice) Rock)
由于_shoot
的类型签名告诉GHC,其第一个参数必须为RPS
值,因此它将知道使用为read
定义的RPS
实例类型,并且一切都应该很好,因为您已经将有效的用户选择限制为这3个特定的字符串。
(请注意,对于较大的程序,有更安全,更好的方式来处理此类事情-请参阅Willem的答案中的一种简单方法-但这对于基本的学习练习来说是很好的。)