为什么我不能这样写这个函数?

Fra*_*aus 1 haskell functional-programming

我正在攻读"功能编程入门"考试.这是我遇到的问题之一:

"以下数据类型用于表示一手牌:

data Suit = Hearts | Clubs | Diamonds | Spades                           
  deriving Eq

data Rank = Numeric Int | Jack | Queen | King | Ace                      
   deriving Eq

data Card = NormalCard Rank Suit | Joker   
  deriving Eq
Run Code Online (Sandbox Code Playgroud)

定义一个函数

countAces:: [Card] -> Int
countAces = undefined
Run Code Online (Sandbox Code Playgroud)

其中countAces返回给定手牌中的牌数或者是aces或jokers.例如,如果手中有三个A和两个笑话,答案将是五个."

所以我想我会这样写:

countAces:: [Card] -> Int
countAces []                               = 0
countAces (c:cs) | c == (NormalCard Ace _) = 1 + countAces (cs)
                 | c == Joker              = 1 + countAces (cs)
                 | otherwise               = countAces (cs)
Run Code Online (Sandbox Code Playgroud)

但这不会编译,我已经明白我不能写c ==(NormalCard Ace _).但是,如果我将功能更改为:

countAces:: [Card] -> Int
countAces []                         = 0
countAces (c : cs) = countCard c + countAces cs
  where countCard Joker              = 1
        countCard (NormalCard Ace _) = 1
        countCard _                  = 0
Run Code Online (Sandbox Code Playgroud)

然后它的作品!所以我的问题是,为什么第一个版本不起作用?

这是错误:

    * Found hole: _ :: Suit
    * In the second argument of `NormalCard', namely `_'
      In the second argument of `(==)', namely `(NormalCard Ace _)'
      In the expression: c == (NormalCard Ace _)
    * Relevant bindings include
        cs :: [Card] (bound at exam.hs:96:14)
        c :: Card (bound at exam.hs:96:12)
        countAces :: [Card] -> Int (bound at exam.hs:95:1)
      Valid substitutions include
        Hearts :: Suit (defined at exam.hs:87:13)
        Clubs :: Suit (defined at exam.hs:87:22)
        Diamonds :: Suit (defined at exam.hs:87:30)
        Spades :: Suit (defined at exam.hs:87:41)
        undefined :: forall (a :: TYPE r).
                     GHC.Stack.Types.HasCallStack =>
                     a
          (imported from `Prelude' at exam.hs:1:1
           (and originally defined in `GHC.Err'))
Run Code Online (Sandbox Code Playgroud)

非常感谢任何花时间阅读本文的人.

che*_*ner 7

在你的守卫中c == (NormalCard Ace _),你没有进行模式匹配; 你试图比较你没有指定的西装c的新值Card.该_是一个"洞",这是一种非值触发一个错误,但确定哪些类型_应该有,这对于调试和开发非常有用.

要进行模式匹配,请使用显式case表达式:

countAces (c:cs) = case c of
                     (NormalCard Ace _) -> 1 + countAces cs
                     Joker -> 1 + countAces cs
                     otherwise -> countAces cs
Run Code Online (Sandbox Code Playgroud)

因为case是表达式而不是语句,所以可以重构它以减少重复:

countAces (c:cs) = countAces cs + case c of
                      (NormalCar Ace _) -> 1
                      Joker -> 0
                      otherwise -> 0
Run Code Online (Sandbox Code Playgroud)

它基本上概括了countCard您在第二次尝试中定义的功能.

  • 你也可以在LHS上进行模式匹配 - `countAces(NormalCard Ace _:cs)= 1 + countAces cs; countAces(小丑:cs)= 1 + countAces cs; countAces(_:cs)= countAces cs` (2认同)

fdr*_*ger 5

这个问题更多的是关于你的意思是什么的心理模型,而不是关于Haskell本身(如果你问Haskell本身,那么答案是:"因为这就是语言是如何工作的").

所以我会尝试吸引你的想象力:

在第一种情况下,您有一个表达式,它将评估为TrueFalse- 两者都是有效结果.您正在使用现有函数执行比较:(==).这个函数有两个值 - 你需要完全提供它们,没有漏洞 - 出于完全相同的原因,为什么你不能写(2 + _) * 10它并期望它评估一个数字.

在第二种情况下,您使用语言构造=.此构造不是返回值的函数.它用于构建定义.当你写作时a = 2,你不是在写一个可能是真或假的表达.您正在定义a来讲2.它将工作并永远是真的 - 或者不会编译.在这种情况下 - 你可以使用洞.当你写作时a _ = 2,你真的说:无论你申请什么,a你都会得到2.