代码之家  ›  专栏  ›  技术社区  ›  Brian Hsu

if/else控制块的良好Haskell编码风格?

  •  30
  • Brian Hsu  · 技术社区  · 16 年前

    我正在学习Haskell,希望它能帮助我更接近函数式编程。以前,我主要使用具有类似C语法的语言,如C、Java和D。

    if / else 控件使用的控制块 tutorial on Wikibooks

    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语言中推荐的风格,我们应该缩进 如果 , else if

    我知道它在Haskell中不起作用,因为如果我缩进,这将是一个解析错误 其他的 如果

    但是下面的样式呢?我认为这比上面的要清楚得多。但是,由于Wikibooks和Haskell的另一个教程(在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!"
    

    8 回复  |  直到 6 年前
        1
  •  27
  •   Greg Bacon    14 年前

    什么 你的计划可以,而不是怎么做。

    在游戏中,您的程序要求用户进行猜测。正确的猜测就是赢家。否则,用户将重试。游戏将继续,直到用户猜对为止,因此我们写道:

    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
    
    <interactive>: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

    IO String 但是 String

    功能 liftM Monad接受一个纯函数并将其提升到Monad中。表达式的类型告诉我们很多关于它的作用:

    *Main> :type read `liftM` getLine
    read `liftM` getLine :: (Read a) => IO a

    这是一个I/O操作,当运行时,它会返回一个使用转换的值 阅读 Int 就我们而言。回想一下 readLine 价值观,所以你可以想到 允许我们申请 阅读 IO 蒙纳德。

    1
    Too low!
    100
    Too high!
    42
    You Win!
        2
  •  8
  •   mattiast    16 年前

    您可以使用“案例”结构:

    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!"
    
        3
  •  8
  •   Peter Burns    16 年前

    mattiast的case语句的一个小改进(我会编辑,但我没有karma)是使用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 journyman的额外问题:什么类型的doGuessing?

        4
  •  4
  •   yfeldblum    14 年前

    if ... then ... else do

    但如果允许的话,许多人更喜欢稍微不同的语法 then else 以与相应的缩进级别相同的缩进级别显示 if . 因此,GHC附带了一个名为 DoAndIfThenElse

    这个 多恩迪弗瑟 在Haskell规范的最新版本中,扩展成为核心语言的一部分, Haskell 2010

        5
  •  3
  •   Erik Hesselink Renaud    16 年前

    请注意,许多人认为必须在“do”块中缩进“then”和“else”这一事实是一个bug。它可能会在Haskell规范的下一个版本Haskell(Haskell prime)中修复。

        6
  •  1
  •   Jonathan Tran    16 年前

    还可以使用带花括号的显式分组。请参阅的布局部分 http://www.haskell.org/tutorial/patterns.html

    不过我不建议这样做。除了在一些特殊情况下,我从未见过有人使用显式分组。我通常看电视 Standard Prelude code

        7
  •  0
  •   mipadi    16 年前

    我使用了一种编码风格,就像您在Wikibooks中的示例一样。当然,它没有遵循C的指导原则,但是Haskell不是C,而且它相当可读,特别是当你习惯了它之后。它还模仿了许多教科书中使用的算法风格,比如Cormen。

        8
  •  0
  •   user61288 user61288    14 年前

    您将看到Haskell的一系列不同缩进样式。如果没有设置为以任何样式精确缩进的编辑器,它们中的大多数都很难维护。

    您显示的样式更简单,对编辑器的要求更低,我认为您应该坚持使用它。我能看到的唯一不一致之处是,您将第一个do放在自己的行上,而将其他do放在then/else之后。