我有这段代码:
module BalancedTwoDozenMultDrill where
import BalancedTwoDozenLib
myRandoms :: Int -> IO [Int]
myRandoms n = let x = 24^n `div` 2 in randomRs (-x,x) <$> getStdGen
drill :: [Int] -> IO ()
drill (x:y:rs) = do
putStr $ showInt x ++ " × " ++ showInt y ++ " = "
a <- getLine
case a of
"" -> return ()
showInt (x * y) -> do -- <= here
putStrLn "Correct"
drill rs
_ -> do
putStrLn $ "Wrong; " ++ showInt (x * y)
drill rs
main :: IO [Int]
main = drill =<< myRandoms =<< readLn
并收到错误:
BalancedTwoDozenMultDrill.hs:11:18: Parse error in pattern: x * y
但是,将部分case语句替换为:
-- ...stuff
let i = showInt (x * y)
case a of
"" -> return ()
i -> do
-- stuff...
使其解析(它转到“不在范围内”错误,我可以修复)。我看到第一个片段错误的唯一原因是有功能应用正在进行中。在case语句中我不能使用普通函数应用程序来替代它吗?
答案 0 :(得分:6)
当你在case语句中有一个模式时,它必须遵循与函数参数模式匹配相同的规则。只能匹配文字,构造函数和通配符_
,而不能匹配函数应用程序。相反,你可以做更像
a <- getLine
let xyStr = showInt (x * y) -- Avoid recomputation with a let binding
when (not $ null a) $ do
if a == xyStr
then do
putStrLn "Correct"
drill rs
else do
putStrLn $ "Wrong; " ++ xyStr
drill rs
但是,您需要从when
导入Control.Monad
。
你必须在case语句中遵循与函数定义中的模式匹配相同的规则是因为编译器实际上转换了像
这样的东西head :: [a] -> a
head (x:xs) = x
head _ = error "Prelude.head: empty list"
向
head :: [a] -> a
head list = case list of
(x:xs) -> x
_ -> error "Prelude.head: empty list"
我们拥有前一版本的唯一原因是方便,它通常会使代码看起来更漂亮。
This链接应该能够让您更全面地解释什么是有效的模式匹配结构。
您遇到的另一个问题是尝试将showInt (x * y)
替换为i
let i = showInt (x * y)
。执行此操作时,首先将值showInt (x * y)
绑定到名称i
,然后在您的case语句中使用模式
"" -> ...
i -> ...
_ -> ...
所以现在你的模式是i
,它在""
之后就像一个包罗万象的模式。这会为该案例陈述的范围重新命名i
。
要记住的一个好规则是,您不能对运行时获得的值进行模式匹配,您必须检查相等或其他比较操作。
答案 1 :(得分:4)
虽然已经接受了答案,但我只是提到在case
表达式中使用布尔表达式有点棘手 - 使用警卫:
case () of
_
| a == "" -> return ()
| showInt (x * y) -> do -- <= here
putStrLn "Correct"
drill rs
| otherwise -> do
putStrLn $ "Wrong; " ++ showInt (x * y)
drill rs