Сравнение функций в Haskell

Есть ли способ сравнить две функции в Haskell?

Моя мысль заключается в том, что ответ отрицательный, поскольку функции не получат class типа Eq. Однако я пытаюсь написать довольно тривиальную функцию, и это похоже на обычную вещь:

search :: ((Enum a) => a -> a) -> Card -> [Card] search op x list = if (op == succ && rank x == King) || (op == pred && rank x == Ace) then [] else let c = [ n | n <- list, rank n == op (rank x)] in if length c == 1 then x : search op (head c) list else [] 

Сообщение об ошибке:

 No instance for (Eq (Rank -> Rank)) arising from a use of `==' 

В основном он либо ищет вверх или вниз список карт, которые ищут совпадение с следующей или предыдущей ранжированной картой от x, создавая список. Принимая функцию «pred» или «succ» в качестве оператора, он работает как вперед, так и назад. Тем не менее, мне нужно проверить, что он не выходит за границы в перечислении, иначе он выдает исключение.

Поэтому я ищу способ предотвратить исключение или решить эту проблему!

Любые другие указатели на улучшение кода также будут оценены 🙂

Спасибо за все замечательные советы, это решение, которое я придумал (взятые бит от каждого ответа действительно!):

EDIT: Правильное решение ниже:

  maybeSucc x | x == maxBound = Nothing | otherwise = Just (succ x) maybePred x | x == minBound = Nothing | otherwise = Just (pred x) -- takes a list of cards which have a rank one op than x -- only if there is exactly one is it sequential, otherwise end matching search :: (Rank -> Maybe Rank) -> Rank -> [Card] -> [Card] search op x list = case filter (\n -> Just (rank n) == op x) list of [y] -> y : search op (rank y) list _ -> [] 

Контрольная работа:

 *Main> let cards = [Card Ace Heart, Card Two Club, Card Three Spade, Card Five Club, Card Four Diamond] *Main> search maybeSucc Two cards [Three of Spades,Four of Diamonds,Five of Clubs] *Main> search maybePred Three cards [Two of Clubs,Ace of Hearts] 

1) Ваш op слишком общий. Вы будете использовать его только для Карт независимо от типа rank (undefined :: Card) , так что просто сделайте его RankThing -> RankThing . Кроме того, в вашей сигнатуре типа функции отсутствует тип возвращаемого значения.

2) Пример предполагаемого использования выглядит так, будто это будет search succ Ace xs , но это довольно неуклюжий, как насчет двух вспомогательных функций, которые обрабатывают границы:

 searchUp King _ = [] searchUp x ys = search succ x ys searchDown Ace _ = [] searchDown x ys = search pred x ys 

Это может показаться лучше для ваших пользователей и избежать необходимости проверять работу

3) если вы действительно хотите проверить, какая операция выполнена, и знаете, что операция будет одной из двух возможностей, тогда вы можете назвать операцию или протестировать ее с помощью известного теста ответа (KAT). Например:

 data Operation = Succ | Pred search :: Operation -> Card -> [Card] -> [] search Succ King _ = [] search Succ x ys = search' succ x ys search Pred Ace _ = [] search Pred x ys = search' pred x ys 

И решение KAT (довольно хромое, imo):

 search op x ys | op Five == Four && rank x == Ace = [] | op Five == Six && rank x == King = [] | otherwise = ... 

Неполный ответ

Существует нет и никогда не будет способа сравнить две функции для равенства. Существует математическое доказательство того, что это вообще невозможно.

«Прагматичные» подходы будут либо зависеть от внутренних действий вашего компилятора (например, если две функции равны, если они представлены одним и тем же адресом памяти внутри) или менее полезны, чем ожидалось. Каков ожидаемый результат succ == (+1)? Как насчет let a == 1 в succ == (+ a)?

Я не могу придумать способ определения (==) для функций, которые всегда имеют смысл, и я не верю никому другому.

Вещи, не имеющие отношения к вопросу

В сигнатуре типа отсутствует тип возврата.

Ваша функция имеет тип ранга 2, который не является стандартным Haskell 98, и, что более важно, в этом случае не требуется. Вам все равно, может ли переданная функция работать с любым типом Enum, вам все равно, что она работает для Rank:

 search :: (Rank -> Rank) -> Card -> [Card] -> [Card] -- much simpler. 

Я бы попытался использовать случай чаще, и если / то реже. В частности, я бы написал:

 case [ n | n <- list, rank n == op (rank x)] of [y] -> x : search op y list -- length == 1 _ -> [] -- otherwise 

Мой реальный ответ

Ваша функция в основном работает только с двумя разными значениями для op, но ее тип не говорит об этом; что мне не нравится.

Вы можете сделать это старомодно:

 data Direction = Succ | Pred deriving(Eq) search :: Direction -> Card -> [Card] -> [Card] search dir x list = if (dir == Succ && rank x == King) ... ... let op = case Direction of Succ -> succ ; Pred -> pred in ... 

Или вы можете сделать параметр более общим и передать функцию, которая может потерпеть неудачу изящно (возвращая Nothing):

 maybeSucc x | x == maxBound = Nothing | otherwise = Just (succ x) search :: (Rank -> Maybe Rank) -> Card -> [Card] -> [Card] search op x list = case op (rank x) of Nothing -> [] Just theRank -> case [ n | n <- list, rank n == theRank ] of ... 

Ну, в Haskell нет понятия «ссылочного равенства», а «поведенческое равенство» функций невозможно проверить в общем случае. Хотя это возможно для функций, работающих только на небольших конечных типах (как Rank ), это обычно неразумно, потому что это приводит к комбинаторному взрыву, если вы не будете осторожны.

Существуют различные способы достижения вашей непосредственной цели, такие как:

  • Вместо непосредственного использования succ и pred используйте функции самозащиты с типом Enum a => a -> Maybe a создает Nothing а не исключение.

  • Передайте значение «stop» для проверки, например, для search op end x list = if x == end then [] ....

  • Забудьте о succ и pred целиком и просто перейдите в список значений для сравнения, например search :: [Rank] -> Card -> [Card] -> [Card] , и завершите поиск, если список рангов пуст ,

Еще пара замечаний по вашему коду:

  • Ваше понимание списка – изобретать колесо – искать стандартные библиотечные функции, такие как filter , которые будут делать то, что вы хотите.

  • Не сравнивайте length списка с небольшим сопоставлением шаблонов с постоянным использованием.

Пока функции находятся над соответствующими доменами и диапазонами (Enum, Bounded, Eq и т. Д., В несколько иной комбинации для домена и диапазона), вы можете сравнить конечные функции, даже более высокие, прямолинейным способом и затем автоматизировать процесс с помощью classов типов.

Я написал эту идею в сообщении в одном из списков рассылки Haskell, а затем (более правильно) в документе, который я представил на одной из конференций FDPE (Victoria, BC). Я никогда не видел, чтобы это делалось раньше, но я не удивлюсь, если другие тоже это сделают: это довольно простая идея, как только вы преодолеете bugaboo, что «функции нельзя сравнивать для равенства». Разумеется, применяются обычные оговорки относительно пристрастности и строгости: например, вы не можете вернуть True для двух функций, которые оба не могут прервать некоторый общий аргумент.

Но для конечных областей этот метод работает и обеспечивает много практического использования. (Плюс это просто забавно использовать :)).

Изменить: я добавил код hpaste здесь ; он работает в Hugs, но нуждается в некоторых настройках для GHC.

Редактирование 2: я добавил прагму и прокомментировал код hpaste-d, чтобы он работал как в GHC, так и в Hugs. Я также добавил этот пример как test3 (он возвращает True, как и ожидалось, и в коротком порядке):

 (\x -> [(&&x), (||x), not]) == (\y -> [(y&&), (y||), not . not . not]) 

Код ниже не может компилироваться, но, надеюсь, вы получите идею:

 data Op = Succ | Pred deriving Eq fromOp :: Enum a => Op -> a -> a fromOp Succ = succ fromOp Pred = pred search :: Op -> Card -> [Card] -> [Card] search op x list = if (op == Succ && rank x == King) || (op == Pred && rank x == Ace) then [] else let c = [ n | n <- list, rank n == (fromOp op) (rank x)] in if length c == 1 then x : search op (head c) list else [] 
  • `String 'применяется к слишком многим аргументам типа
  • Что означает $ mean / do в Haskell?
  • Что такое «n + k patterns» и почему они запрещены в Haskell 2010?
  • Что такое class Comonad в Haskell?
  • Сравнение веб-фреймворков Haskell Snap и Yesod
  • Haskell: Что такое нормальная форма слабой головы?
  • Haskell: списки, массивы, векторы, последовательности
  • Lambda для выражений типа в Haskell?
  • Почему я не должен смешивать вкладки и пробелы?
  • Смущает смысл classа «Альтернативный» и его отношение к другим типам classов
  • Haskell: как оценить строку типа «1 + 2»
  • Давайте будем гением компьютера.