Haskell, confrontando un generico con un Char

Nov 06 2020

Devo creare una funzione Haskell che abbia un input generico ma restituisca un booleano a seconda che il generico corrisponda a determinate cifre / caratteri qualcosa sulla falsariga di:

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

Come questo non funzionerà perché non puoi confrontare Char con il generico. Ho cercato per anni e sono nuovo su Haskell, quindi non ho idea di come risolverlo. Nota: è specificato specificamente che l'ingresso deve essere un -> Bool

Ad esempio questo codice funziona perfettamente:

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

Tuttavia ho bisogno di "un" invece di Char. Ci scusiamo per l'eventuale confusione. (Apparentemente "a" non è un generico)

Risposte

3 leftaroundabout Nov 06 2020 at 02:43

Una possibile interpretazione della domanda è: vuoi che la funzione dica se l'argomento è a Chare nell'elenco . Non puoi farlo con la a -> Boolsola firma , perché le informazioni sul tipo non sono disponibili per le funzioni, tuttavia puoi richiedere esplicitamente le informazioni sul tipo: c'è una classe dedicata per questo .

import Data.Typeable

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

(Nota che, a differenza di molte altre lingue, castnon converte realmente i tipi , ma corrisponde solo se era il tipo di destinazione in primo luogo.)

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

Di per sé, una funzione di tipo a -> Bool, che è l'abbreviazione di forall a. a -> Bool, non è molto utile, poiché deve funzionare "per tutti a" che il chiamante specifica, quindi non è consentito sapere nulla a. Ecco perché ricevi risposte che dicono fondamentalmente "non puoi arrivarci da qui".

(In effetti, ci sono solo due funzioni totali pure con questo tipo: alwaysTrue _ = Truee alwaysFalse _ = False.)

Ma immagino, sulla base di questo tuo commento:

Ho bisogno di una funzione che abbia un input :: a -> Boole deve calcolare se aè un elemento in un elenco specificato.

Che la tua domanda è nel contesto di una funzione come questa:

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

Ora possiamo fare qualcosa di utile! Vedi, questo tipo è l'abbreviazione di forall a. (a -> Bool) -> [a] -> Bool, vale a dire, opera anche su valori di qualsiasi tipo a, ma ancora una volta, il chiamante di questa funzione è colui che sceglie ae passa nel predicato di tipo a -> Bool per alcuni specificia , così come un elenco di avalori. Quindi containsnon sa nulla a, ma fa sapere che può applicare il predicato agli elementi della lista, dato che sono garantiti per essere dello stesso tipo.

Quindi, ad esempio, puoi chiamare questa funzione con un argomento di tipo Char -> Bool, che imposta aa Char, o uno di tipo Int -> Bool, che imposta aa Int, e così via:

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

Se vuoi scriverecontains tu stesso questa funzione, puoi usare una definizione ricorsiva, con questa struttura generale:

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?
  [] -> _

I caratteri di sottolineatura sono chiamati buchi digitati , che puoi includere nel tuo programma per chiedere a GHC di dirti che tipo si aspetta lì e darti alcuni suggerimenti per funzioni e variabili nell'ambito che potrebbero adattarsi. Man mano che capisci come inserire più dettagli del tuo programma, puoi usare più fori per chiedere i tipi di parti più specifiche, ad esempio potresti sostituire il primo foro con if _ then _ else _.

Qualcosa che può essere utile qui è l' TypeApplicationsestensione (abilitata in GHCi con :set -XTypeApplicationso in un file sorgente con {-# LANGUAGE TypeApplications #-}), che ti consente di scrivere esplicitamente gli argomenti del tipo, ad esempio in 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

E cosa ti piace fare se aè qualcosa del genere b -> FreeT (PipeF a b)?

Il tuo tipo dice che posso consegnarti qualsiasi funzione e ti verrà in mente che Boolci sono solo due modi per scrivere questa funzione. checkFunct _ = TrueecheckFunct _ = False

Devi dire qualcosa di più al compilatore su cosa ti aspetti come input.

Dalla tua implementazione di esempio, immagino che potresti voler vedere se la rappresentazione di stringa di x è in un elenco?

Potresti scrivere:

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

l' Show a =>dice che tutto ciò che io do a come input a questa funzione, show adeve restituire una stringa.

O forse vuoi scrivere la tua classe

class ToChar a where
  toChar :: a -> Char

Potresti quindi scrivere:

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

Per usare 'checkFunct' dovresti fornire un'implementazione di toChar per qualsiasi tipo tu voglia usare con quella funzione.

import Data.Char
instance ToChar Int where
  toChar = chr