如何使用putStrLn的后卫?

时间:2018-03-27 23:39:19

标签: haskell

我正在尝试编写一个能够执行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!"

2 个答案:

答案 0 :(得分:5)

有一组有限的地方你可以使用警卫 - 它们最常用于功能定义。在这种情况下,您可能正在寻找case语句:

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

_模式匹配任何东西,其中&#34; mops up&#34;剩下的模式。  它在这里的用法类似于otherwise,虽然otherwise实际上只是true的语法糖,因为守卫中的表达式是boolean

它与守卫不完全相同,因为守卫评估布尔表达式而case进行模式匹配,但它有效。更准确的双重守卫是if表达式,但case语法更好,当您可以使用它时。有关更多示例,请查看Wiki上的control structures页面。

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

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

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

答案 1 :(得分:1)

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

{-# 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!"

通常情况下,保护语法| condition仅适用于两个地方: definitions (其中一个保护在名称和参数之后,在=符号之前)和case(在模式之后,在->符号之前):

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!"
    
  5. #1最常用于匹配;如果您需要保护并且想要避免MultiWayIf支持旧编译器,或者只是想将逻辑分解为单独的定义以便于阅读,则使用#2; #3和#4不是很常见或惯用,但它们并没有“错误”。