Сравнение функций в 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 []
Сообщение об ошибке:
- Интегральные операторы quot vs. div
- Как составить `не` с функцией произвольной arity?
- Обработка исключений в Haskell
- Какие символы разрешены для операторов haskell?
- Кабал не устанавливает зависимости при необходимости профилирования библиотек?
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]
- Что такое монада?
- foldl хвост рекурсивный, так как же foldr работает быстрее, чем foldl?
- Почему бы не быть навязчивым?
- Как преобразовать список в кортеж в Haskell?
- двойной stream для предотвращения ненужной memoization?
- Неверный порядок операций ввода-вывода с использованием putStr и getLine
- Есть ли список расширений GHC, которые считаются «безопасными»?
- Что плохого в ленивом вводе-выводе?
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 []