2010-01-28 4 views
3

Considérez ce qui suit:Dans Haskell, existe-t-il un moyen de faire "elem" sur un constructeur avec un argument?

data A = A1 | A2 Integer 
x = [A1, A2 999] 
elem A1 x == True 

est-il un moyen de faire le test suivant?

elem (A2 _) x == True 
+1

Semble que j'ai trouvé une solution alternative pour cela: ([z | (A2 z) <- x]/= []) – me2

+6

Si vous voulez être nitpicky, il vaut mieux utiliser 'null' plutôt que '/ = [] 'car il ne nécessite pas la contrainte Ord: not $ null [() | A2 _ <- x] – Martijn

Répondre

1
instance Eq A where 
    A1 == A1 = True 
    A2 _ == A2 _ = True 
    _ == _ = False 

elem (A2 undefined) x == True 

Bien sûr, cela a des effets au-delà de ce que vous avez demandé.

+0

Dans mon cas, je pense que ([z | (A2 z) <- x]/= []) est le plus simple car je ne dois l'utiliser qu'une fois mais je pense que définir une instance de A est probablement l'alternative la plus appropriée. Merci, éphémère. – me2

+1

@ me2: N'utilisez pas '/ = []'; Au lieu de cela, utilisez 'non. null'. Voir http://www.haskell.org/haskellwiki/Haskell_programming_tips#Don.27t_ask_for_the_length_of_a_list_when_you_don.27t_need_it pour la justification. – ephemient

8

Non, mais vous pourriez en reformuler l'utilisation.

hasA2 :: [A] -> Bool 
hasA2 = any $ \x -> case x of { A2 _ -> True ; _ -> False } 
4

Non, pour deux raisons:

  1. Pas de support syntaxique pour les expressions de ce genre.

  2. elem nécessitant un premier argument concret à rechercher dans la liste transmise en tant que second argument (donc vous ne pouvez pas le faire avec une correction mineure à la seule syntaxe).

Vous pouvez utiliser Data.List.find à la place:

import Data.List 
isA2 A1  = False 
isA2 (A2 _) = True 
find isA2 [A1, A2 999] 
-- => Just (A2 999) 

Mise à jour: Eh bien, Dietrich m'a battu à lui, mais je vais laisser cette réponse ici pour la solution alternative (plus l'explication sur le dessus, FWIW).

2

Une solution utilisant le modèle Haskell:

{-# LANGUAGE TemplateHaskell #-} 
import Data.ADT.Getters 
import Data.Maybe 

data A = A1 | A2 Integer 
$(mkADTGetters ''A) 

hasA2 = any (isJust . gA2) 

Note: Data.ADT.Getters est dans le paquet "peakachu" de hackage, temporairement. Cette fonctionnalité sera ajoutée à "derive", puis supprimée de "peakachu".

2

Pour développer la réponse de Dietrich:

Si vous voulez simplement correspondre à un nom de constructeur, sans tenir compte tous les champs, vous pouvez utiliser ce modèle: A2 {}.

hasA2 :: [A] -> Bool 
hasA2 = any $ \x -> case x of { A2 {} -> True; _ -> False } 

Cette fonction continuera à travailler si vous décidez ultérieurement d'ajouter un autre champ au constructeur A2.


Et, si vous utilisez ephemient Eq exemple, vous pouvez également l'appeler comme ceci:

elem (A2 {}) 

Encore une fois, sans tenir compte tous les champs (les) en bas d'initialisation.

1

Oui, c'est faisable! Et si vous n'aimez pas haskell de modèle (utilisé dans une autre réponse) alors vous pouvez utiliser les outils "DrIFT" ou "dériver".

{-! global : is !-} 
data Foo = A1 | A2 Int deriving (Show, Eq, Ord) 

hasA2 :: [Foo] -> Bool 
hasA2 = any isA2 

C'est le code que vous tapez et dans le cadre du pipleine du compilateur vous exécutez la source à travers DÉRIVE qui génère les est fonctions * pour tous les types de données dans le module:

{-* Generated by DrIFT : Look, but Don't Touch. *-} 
isA1 (A1) = True 
isA1 _ = False 
isA2 (A2 _) = True 
isA2 _ = False 

Sinon, vous pouvez utilisez "dériver". Utilisez le code ci-dessus, supprimez le {-! ! ... -} directive et insérer:

{-! 
deriving instance Is Foo 
!-} 

ou vous pourriez économiser quelques secondes et modifier le code original à lire:

data Foo = A1 | A2 Int deriving (Show, Eq, Ord {-! Is !-}) 

Et "dérivent" va générer:

isA1 :: Foo -> Bool 
isA1 (A1{}) = True 
isA1 _ = False 

isA2 :: Foo -> Bool 
isA2 (A2{}) = True 
isA2 _ = False 

derive vit sur Hackage tandis que DrIFT peut être trouvé ailleurs.

Questions connexes