Haskell, сравнивая общий с Char

Nov 06 2020

Мне нужно создать функцию Haskell, которая имеет общий ввод, но выводит логическое значение в зависимости от того, соответствует ли универсальный определенным цифрам / символам что-то вроде строк:

checkFunct :: a -> Bool
checkFunct x = (elem x ['a','b','c'])

Как это не сработает, потому что вы не можете сравнить Char с общим. Я долго искал, и я новичок в Haskell, поэтому понятия не имею, как это исправить. Примечание: в нем конкретно указано, что ввод должен быть -> Bool

Например, этот код отлично работает:

checkFunct :: Char -> String
p x = elem x ["a","b","c"]

Однако мне нужна вставка Char. Извините за путаницу. (По-видимому, «а» не является универсальным)

Ответы

3 leftaroundabout Nov 06 2020 at 02:43

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

import Data.Typeable

checkFunct :: Typeable a => a -> Bool
checkFunct x = case cast x of
    Just xc  -> xc `elem` ['a','b','c']
    Nothing  -> False

(Обратите внимание, что, в отличие от многих других языков, на castсамом деле не преобразует типы , а просто соответствует тому, был ли это целевой тип с самого начала.)

*Main> checkFunct 'b'
True
*Main> checkFunct 'f'
False
*Main> checkFunct 37
False
*Main> checkFunct "a"
False
3 JonPurdy Nov 06 2020 at 04:05

Сама по себе функция типа a -> Bool, сокращенно от слова forall a. a -> Bool, бесполезна, поскольку она должна работать «для всех a», указанных вызывающей стороной, поэтому ей не разрешено ничего знать a. Вот почему вы получаете ответы, в которых в основном говорится: «отсюда вам не добраться».

(На самом деле, с этим типом есть только две чистые итоговые функции: alwaysTrue _ = Trueи alwaysFalse _ = False.)

Но я предполагаю, основываясь на вашем комментарии:

Мне нужна функция, которая имеет ввод, :: a -> Boolи ей нужно вычислить, aявляется ли элемент в указанном списке.

Что ваш вопрос находится в контексте такой функции:

contains :: (a -> Bool) -> [a] -> Bool

Теперь мы можем сделать что-нибудь полезное! Видите ли, этот тип является сокращением forall a. (a -> Bool) -> [a] -> Bool, то есть он также работает со значениями любого типа a, но, опять же, вызывающий эту функцию - тот, кто выбирает aи передает предикат типа a -> Bool для некоторого конкретногоa , а также список aценностей. Так что containsничего не знаю о a, но это действительно знает , что он может применить предикат к элементам списка, поскольку они гарантированно одного и того же типа.

Так, например , вы можете вызвать эту функцию с аргументом типа Char -> Bool, который устанавливает aк Char, или одного типа Int -> Bool, который устанавливает aв Int, и так далее:

isABC :: Char -> Bool
isABC x = x `elem` "abc"

-- contains :: (a -> Bool) -> [a] -> Bool
-- contains isABC :: [Char] -> Bool
-- contains isABC "yeah" == True
-- contains isABC "nope" == False

is123 :: Int -> Bool
is123 x = x >= 1 && x <= 3

-- contains is123 :: [Int] -> Bool
-- contains is123 [] == False
-- contains is123 [0] == False
-- contains is123 [0, 1] == True

Если вы хотите написать эту containsфункцию самостоятельно, вы можете использовать рекурсивное определение с такой общей структурой:

contains :: (a -> Bool) -> [a] -> Bool
contains predicate list = case list of

  -- How can you test the current element with the predicate?
  -- What should you do when the predicate returns true?
  -- How can you test the rest of the list?
  current : rest -> _

  -- What should you return if you’ve reached the end?
  [] -> _

Подчеркивания называются типизированными отверстиями , которые вы можете включить в свою программу, чтобы попросить GHC сообщить вам, какой тип он там ожидает, и дать вам несколько предложений по функциям и переменным в области видимости, которые могут подойти. По мере того, как вы выясняете, как заполнить больше деталей вашей программы, вы можете использовать больше отверстий, чтобы спросить типы более конкретных деталей, например, вы можете заменить первое отверстие на if _ then _ else _.

Что-то, что может быть здесь полезно, - это TypeApplicationsрасширение (включено в GHCi с помощью :set -XTypeApplicationsили в исходном файле с помощью {-# LANGUAGE TypeApplications #-}), которое позволяет вам явно записывать аргументы типа, например, в GHCi:

> :set -XTypeApplications

> contains :: (a -> Bool) -> [a] -> Bool; contains = error "TODO: implement 'contains'"

> :type contains @Char
contains @Char :: (Char -> Bool) -> [Char] -> Bool

> :type contains @Int
contains @Int :: (Int -> Bool) -> [Int] -> Bool

> :t map
map :: (a -> b) -> [a] -> [b]

> :t map @Double
map @Double :: (Double -> b) -> [Double] -> [b]

> :t map @Double @Int
map @Double @Int :: (Double -> Int) -> [Double] -> [Int]
1 JohnF.Miller Nov 06 2020 at 02:37

А чем вы любите заниматься, если aчто-то нравится b -> FreeT (PipeF a b)?

Ваш тип говорит, что я могу передать вам что угодно, и вы придумаете, Boolчто есть только два способа написать эту функцию. checkFunct _ = TrueиcheckFunct _ = False

Вам нужно сообщить компилятору немного больше о том, что вы ожидаете от ввода.

Из вашего примера реализации, я предполагаю, что вы, возможно, захотите увидеть, находится ли строковое представление x в списке?

Вы могли написать:

checkFunct :: Show a => a -> Bool
checkFunct x = (elem (show x) ["a","b","c"])

Show a =>говорит , что все , что я даю в качестве вклада в эту функцию, show aдолжна возвращать строку.

Или, возможно, вы хотите написать свой собственный класс

class ToChar a where
  toChar :: a -> Char

Затем вы можете написать:

checkFunct :: ToChar a=> a -> Bool
checkFunct x = (elem (toChar x) ['a','b','c'])

Чтобы использовать checkFunct, вам необходимо предоставить реализацию toChar для любого типа, который вы хотите использовать с этой функцией.

import Data.Char
instance ToChar Int where
  toChar = chr