Haskell: 你如何检查 IO 上的运行时类型?
Haskell: how do you check runtime types on IO?
我正在 Haskell 上完成一些介绍性的 material 并尝试完成这个愚蠢的石头剪刀布的命令行实现。
我认为输入的类型保护足以让编译器相信输入是 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."
您没有将 choice
(即 String
)转换为 RPS
,甚至更好的 Maybe RPS
:
readRPS :: String -> Maybe RPS
readRPS "rock" = Just Rock
readRPS "paper" = Just Paper
readRPS "scissors" = Just Scissors
readRPS _ = Nothing
这里我们因此 return 一个 Just x
给定输入是有效的(x
相应的 RPS
项),或者 Nothing
如果字符串不是有效选项。
然后我们可以将其实现为:
import Data.Char(toLower)
main :: IO ()
main = do
putStrLn "Rock, Paper, or Scissors?"
choice <- getLine
case <b>readRPS (map toLower choice)</b> of
<b>Just rps</b> -> putStrLn (_shoot rps Rock)
Nothing -> putStrLn "Invalid choice."
main
您就快完成了,您只需要 read 函数将用户的字符串转换为您的 RPS
数据类型。
您需要做的第一件事是使 RPS
成为 Read
类型类的一个实例。这可以通过将您的 data
声明修改为:
来轻松完成
data RPS = Rock | Paper | Scissors deriving Read
deriving Read
所做的是给 RPS
一个 Read
类型类的默认实例,它以显而易见的方式工作:read "Rock"
将变成 Rock
依此类推,前提是编译器知道您在需要 RPS
类型值的上下文中使用 read
。
然后您需要做的就是在您的 main
函数中更改此内容:
putStrLn (_shoot choice Rock)
至
putStrLn (_shoot (read choice) Rock)
因为 _shoot
有一个类型签名告诉 GHC 它的第一个参数必须是一个 RPS
值,它会知道使用为你的 [=13] 定义的 read
的实例=] 类型,一切都应该很好,因为您已经将有效的用户选择限制为这 3 个特定字符串。
(请注意,对于较大的程序,有更安全和更好的方法来处理此类事情 - 请参阅 Willem 对一种简单方法的回答 - 但这对于基本学习练习来说很好。)
我正在 Haskell 上完成一些介绍性的 material 并尝试完成这个愚蠢的石头剪刀布的命令行实现。
我认为输入的类型保护足以让编译器相信输入是 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."
您没有将 choice
(即 String
)转换为 RPS
,甚至更好的 Maybe RPS
:
readRPS :: String -> Maybe RPS
readRPS "rock" = Just Rock
readRPS "paper" = Just Paper
readRPS "scissors" = Just Scissors
readRPS _ = Nothing
这里我们因此 return 一个 Just x
给定输入是有效的(x
相应的 RPS
项),或者 Nothing
如果字符串不是有效选项。
然后我们可以将其实现为:
import Data.Char(toLower)
main :: IO ()
main = do
putStrLn "Rock, Paper, or Scissors?"
choice <- getLine
case <b>readRPS (map toLower choice)</b> of
<b>Just rps</b> -> putStrLn (_shoot rps Rock)
Nothing -> putStrLn "Invalid choice."
main
您就快完成了,您只需要 read 函数将用户的字符串转换为您的 RPS
数据类型。
您需要做的第一件事是使 RPS
成为 Read
类型类的一个实例。这可以通过将您的 data
声明修改为:
data RPS = Rock | Paper | Scissors deriving Read
deriving Read
所做的是给 RPS
一个 Read
类型类的默认实例,它以显而易见的方式工作:read "Rock"
将变成 Rock
依此类推,前提是编译器知道您在需要 RPS
类型值的上下文中使用 read
。
然后您需要做的就是在您的 main
函数中更改此内容:
putStrLn (_shoot choice Rock)
至
putStrLn (_shoot (read choice) Rock)
因为 _shoot
有一个类型签名告诉 GHC 它的第一个参数必须是一个 RPS
值,它会知道使用为你的 [=13] 定义的 read
的实例=] 类型,一切都应该很好,因为您已经将有效的用户选择限制为这 3 个特定字符串。
(请注意,对于较大的程序,有更安全和更好的方法来处理此类事情 - 请参阅 Willem 对一种简单方法的回答 - 但这对于基本学习练习来说很好。)