Почему я не могу написать такую ​​функцию? - PullRequest
0 голосов
/ 23 декабря 2018

Я готовлюсь к экзамену "Введение в функциональное программирование".Это одна из проблем, на которой я застрял:

"Для представления комбинации карт используются следующие типы данных:

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

Определение функции

countAces:: [Card] -> Int
countAces = undefined

где countAces возвращает количество карт в данной руке, которые являются тузами или джокерами. Так, например, если в руке три туза и два джокера, ответ будет пять. "

Итак, яЯ подумал, что напишу так:

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

Но это не скомпилируется, и я понял, что не могу написать 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

Тогда это сработает!Поэтому мой вопрос: почему не работает первая версия?

Это ошибка:

    * 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'))

Огромное спасибо всем, кто нашел время, чтобы прочитать это.

Ответы [ 3 ]

0 голосов
/ 23 декабря 2018

Этот вопрос больше относится к вашей ментальной модели того, что значат вещи, а не к самому Haskell (если вы спросите о самом Haskell, тогда ответ будет: «потому что так работает язык»).

Поэтому я постараюсь просто обратиться к вашему воображению:

В первом случае у вас есть выражение, которое оценивается как True или False - оба являются действительными результатами.Вы выполняете сравнение, используя существующую функцию: (==).Эта функция принимает два значения - и вам нужно предоставить их полностью, без дырок - по той же самой причине, почему вы не можете написать (2 + _) * 10 и ожидать, что она будет вычисляться как число.

Во втором случае вы используете языковую конструкцию =.Эта конструкция не является функцией, которая возвращает значение.Он используется для построения определения .Когда вы пишете a = 2, вы не пишете выражение, которое может быть истинным или ложным.Вы определяете a в терминах 2.Он либо будет работать и будет всегда верным - либо не будет компилироваться.В этом контексте - вы можете использовать дыры.Когда вы пишете a _ = 2, вы действительно говорите: независимо от того, что вы подаете в a, вы получите 2.

0 голосов
/ 23 декабря 2018

Не просматривая руководства, я предполагаю, что с "==" вы сравниваете значения и здесь то, что у вас есть разные структуры.Во втором решении вы используете сопоставление с шаблоном по структуре.

Когда вы определяете абстрактный тип данных в Haskell с альтернативными структурами, «производного уравнения» недостаточно для сравнения этих различных структур, вам придетсядать определение равенства.Вы используете «==» так, как вы указали в первом случае, если определите «==» для двух элементов Card.

0 голосов
/ 23 декабря 2018

На страже c == (NormalCard Ace _) вы не выполняете сопоставление с образцом;Вы пытаетесь сравнить c с новым значением Card, костюм которого вы не указали._ - это «дыра», которая является ненулевым значением, которое вызывает ошибку, но определяет, какой тип должен иметь _, что полезно для отладки и разработки.

Для сопоставления с образцом используйте явное выражение case:

countAces (c:cs) = case c of
                     (NormalCard Ace _) -> 1 + countAces cs
                     Joker -> 1 + countAces cs
                     otherwise -> countAces cs

Поскольку case является выражением, а не оператором, вы можете изменить его, чтобы уменьшить некоторое повторение:

countAces (c:cs) = countAces cs + case c of
                      (NormalCar Ace _) -> 1
                      Joker -> 0
                      otherwise -> 0

, который в основном включает функцию countCard, которую вы определили во второй попытке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...