我正在学习Haskell,希望它能帮助我更接近函数式编程.以前,我主要使用类似C语法的语言,如C,Java和D.
关于Wikibooks教程使用的if
/ else
control块的编码风格,我有一点疑问.代码如下所示:
doGuessing num = do putStrLn "Enter your guess:" guess <- getLine if (read guess) < num then do putStrLn "Too low!" doGuessing num else if (read guess) > num then do putStrLn "Too high!" doGuessing num else do putStrLn "You Win!"
这让我感到困惑,因为这种编码风格完全违背了类似C语言,我们应该缩进推荐款式if
,else if
以及else
在同一列.
我知道它只是在Haskell中不起作用,因为如果我else
在同一列缩进,那将是一个解析错误if
.
但是以下风格怎么样?我认为它比上面的要清楚得多.但由于以上内容被Wikibooks和Yet Another Haskell Tutorial(在Haskell官方网站上标记为"最佳在线教程")使用,我不确定这种编码风格是否是Haskell程序中的约定.
doGuessing num = do putStrLn "Enter your guess:" guess <- getLine if (read guess) < num then do putStrLn "Too low!" doGuessing num else if (read guess) > num then do putStrLn "Too high!" doGuessing num else do putStrLn "You Win!"
所以,我很想知道更常使用哪种编码风格 - 或者这段代码是否有其他编码风格?
Haskell风格是功能性的,而不是必要的!而不是"做那么,"想想组合功能和描述什么程序会做,而不是如何.
在游戏中,您的程序会要求用户猜测.正确的猜测是赢家.否则,用户再次尝试.游戏一直持续到用户猜对了,所以我们写道:
main = untilM (isCorrect 42) (read `liftM` getLine)
这使用一个重复运行一个动作的组合器(getLine
拉动一行输入并read
在这种情况下将该字符串转换为整数)并检查其结果:
untilM :: Monad m => (a -> m Bool) -> m a -> m () untilM p a = do x <- a done <- p x if done then return () else untilM p a
谓词(部分应用于main
)根据正确的值检查猜测并作出相应的响应:
isCorrect :: Int -> Int -> IO Bool isCorrect num guess = case compare num guess of EQ -> putStrLn "You Win!" >> return True LT -> putStrLn "Too high!" >> return False GT -> putStrLn "Too low!" >> return False
在玩家正确猜测之前要运行的动作是
read `liftM` getLine
为什么不保持简单,只是组成这两个功能?
*Main> :type read . getLine:1:7: Couldn't match expected type `a -> String' against inferred type `IO String' In the second argument of `(.)', namely `getLine' In the expression: read . getLine
类型getLine
是IO String
,但read
想要纯粹String
.
liftM
Control.Monad中的函数采用纯函数并将其"提升"为monad.表达式的类型告诉我们它的作用很多:
*Main> :type read `liftM` getLine read `liftM` getLine :: (Read a) => IO a
这是一个I/O操作,在运行时会给我们一个转换后的值read
,Int
在我们的例子中.回想一下,这readLine
是一个产生String
值的I/O操作,所以你可以认为liftM
允许我们read
在IO
monad中" 应用" .
游戏示例:
1 Too low! 100 Too high! 42 You Win!
你可以使用"case"--construct:
doGuessing num = do putStrLn "Enter your guess:" guess <- getLine case (read guess) of g | g < num -> do putStrLn "Too low!" doGuessing num g | g > num -> do putStrLn "Too high!" doGuessing num otherwise -> do putStrLn "You Win!"
对mattiast的case语句的一个小改进(我编辑,但我缺乏业力)是使用compare函数,它返回三个值之一,LT,GT或EQ:
doGuessing num = do putStrLn "Enter your guess:" guess <- getLine case (read guess) `compare` num of LT -> do putStrLn "Too low!" doGuessing num GT -> do putStrLn "Too high!" doGuessing num EQ -> putStrLn "You Win!"
我真的很喜欢这些Haskell问题,我鼓励其他人发帖.通常你觉得有有要表达你在想什么更好的办法,但Haskell是最初如此陌生,没有什么会浮现在脑海中.
Haskell journyman的奖金问题:doGuessing的类型是什么?