【问题标题】:How do I use a guard with putStrLn?如何在 putStrLn 中使用警卫?
【发布时间】:2018-03-27 23:39:18
【问题描述】:

我正在尝试编写一个函数来执行 2 种不同的游戏模式,这些模式被定义为 tictactoe :: IO ()main :: IO ()。我在输入“|”上遇到解析错误。我不明白我做错了什么。有人可以向我解释一下吗?

tictac :: IO()
tictac = do 
       putStrLn "Would you like to play against the computer or 
                 another player? Enter: 2 Player or Computer"
       choice <- getLine
         |choice == "Computer" = main
         |choice == "2 Player" = tictactoe
         |otherwise = putStrLn "That's not a choice!"

【问题讨论】:

    标签: haskell


    【解决方案1】:

    您可以使用守卫的地方有限 - 它们最常用于函数定义中。在这种情况下,您可能正在寻找 case 语句:

    choice <- getLine
    case choice of
         "Computer" -> main
         "2 Player" -> tictactoe
          _         -> putStrLn "That's not a choice!"
    

    _ 模式匹配任何东西,它会“清除”剩余的模式。 它在这里的使用类似于otherwise,尽管otherwise 实际上只是true 的语法糖,因为守卫中的表达式是boolean

    这与守卫不太一样,因为守卫评估布尔表达式,而case 进行模式匹配,但它可以工作。 if 表达式是更准确的双重保护,但是当您可以使用 case 语法时,它会更好。更多示例请查看 wiki 上的 control structures 页面。


    正如 cmets 中所指出的,也可以在 case 表达式中使用守卫 - 您可以在 the specificationthis question/answer 中看到这一点。它确实需要至少一个模式匹配,这在这里很丑 - 你可以使用 "hack" described here 来做类似的事情:

    case () of
     _ | choice == "Computer" -> main
       | choice == "2 Player" -> tictactoe
       | otherwise            -> putStrLn "That's not a choice!"
    

    但是这样做没有任何好处。

    【讨论】:

    • 你可以在除顶级函数之外的其他地方使用守卫。 case,在wherelet中定义函数,甚至在let中定义不带参数的值。
    • @Potato44 感谢您指出可以在case 中使用守卫,我以前没有见过这种语法。我已经更改了开头,以明确我在谈论所有功能,而不仅仅是顶级功能。我找不到你最后一个案例的例子,你介意给我举个例子吗?
    • 您提到的"hack" 链接的提问者在let 的值brainiac 上使用它。
    • case () of _ | choice == "Computer" -&gt; … | choice == "2 Player" -&gt; … | … 可以使用 MultiWayIf 扩展名替换为 if | choice == "Computer" -&gt; … | choice == "2 Player" -&gt; … | …,该扩展名从 GHC 7.6 开始提供。
    • 我不喜欢守卫的地方是他们在布尔条件之后使用'='符号。这不是一个方程式,而是一个结果,一个箭头。
    【解决方案2】:

    有几种方法可以测试这样的值,但与您编写的最相似的方法是使用名为 MultiWayIf 的扩展程序,自 GHC 7.6(2012 年 9 月)起可用。添加编译指示:

    {-# LANGUAGE MultiWayIf #-}
    

    到源文件的顶部(或在 GHCi 中使用 :set -XMultiWayIf),您可以编写以下内容:

    choice <- getLine
    -- Simply add the ‘if’ keyword here.
    if
      | choice == "Computer" -> main
      | choice == "2 Player" -> tictactoe
      | otherwise -> putStrLn "That's not a choice!"
    

    通常,保护语法| <i>condition</i> 仅在两个地方有效:定义(其中一个保护位于名称和参数之后,= 符号之前)和case(其中它在模式之后,-&gt; 符号之前):

    doubleIfEven :: Int -> Int
    -- Definition
    doubleIfEven x
      | even x = x * 2
      --------
      | otherwise = x
      -----------
    
    doubleIfJustEven :: Maybe Int -> Maybe Int
    doubleIfJustEven mx
      -- Match
      = case mx of
        Just x
          | even x -> Just (x * 2)
          --------
          | otherwise -> Just x
          -----------
        Nothing -> Nothing
    

    以下是替代方案:

    1. case 表达式,在这种情况下,您只测试字符串的(结构)相等性:

      case choice of
        "Computer" -> main
        "2 Player" -> tictactoe
        _ -> putStrLn "That's not a choice!"
      
    2. where 子句或let 绑定中的本地定义:

      tictac :: IO ()
      tictac = do 
        putStrLn "Would you like to play against the computer or\
                 \ another player? Enter: 2 Player or Computer"
        choice <- getLine
        check choice
      
        where
          check choice
            | choice == "Computer" = main
            | choice == "2 Player" = tictactoe
            | otherwise = putStrLn "That's not a choice!"
      
      ----
      
      tictac :: IO ()
      tictac = do 
        putStrLn "Would you like to play against the computer or\
                 \ another player? Enter: 2 Player or Computer"
        let
          check choice
            | choice == "Computer" = main
            | choice == "2 Player" = tictactoe
            | otherwise = putStrLn "That's not a choice!"
        choice <- getLine
        check choice
      
    3. 嵌套的if 表达式:

      if choice == "Computer" then main
        else if choice == "2 Player" then tictactoe
        else putStrLn "That's not a choice!"
      
    4. case 带有虚拟模式和守卫(一个古老的成语):

      case () of
        _ | choice == "Computer" -> main
          | choice == "2 Player" -> tictactoe
          | otherwise -> putStrLn "That's not a choice!"
      

    #1 最常用于匹配; #2 用于如果您需要守卫并且想要避免使用MultiWayIf 来支持旧编译器,或者只是想将逻辑分解到单独的定义中以提高可读性; #3 和 #4 不是很常见或惯用语,但它们并没有什么“错误”。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-01-13
      • 2014-06-19
      • 1970-01-01
      • 2017-04-30
      • 1970-01-01
      • 2020-12-14
      • 1970-01-01
      • 2017-05-08
      相关资源
      最近更新 更多