11
votes

Est-il préférable d'utiliser des gardes que des motifs pour des fonctions de récursivité dans HASKELLL?

Je m'interrogee simplement sur une fonction de récursion que je pose à Haskell. Est-il généralement préférable d'utiliser des gardes que des motifs pour des fonctions de récursivité?

Je ne suis tout simplement pas sûr de la meilleure mise en page, mais je sais que les motifs sont meilleurs pour définir des fonctions telles que ceci: p> xxx pré>

est beaucoup préféré à

f y [] = [] 
f 0 (x:xs) = 
f y (x:xs) =


0 commentaires

5 Réponses :


4
votes

une règle simple

  • Si vous recouvrez une structure de données, utilisez le motif correspondant
  • Si votre condition récursive est plus complexe, utilisez des gardes.

    discussion

    fondamentalement, cela dépend du test que vous souhaitez faire pour protéger la récursivité. S'il s'agit d'un test sur la structure d'un type de données, utilisez la correspondance des motifs, car elle sera plus efficace que les tests redondants pour l'égalité.

    Pour votre exemple, la correspondance des schémas sur les entiers est évidemment propre et plus efficace: xxx

    est identique à des appels récursifs sur n'importe quel type de données, où Vous distinguez les cas via la forme des données.

    Maintenant, si vous aviez des conditions logiques plus compliquées, les gardes auraient un sens.


6 commentaires

Bon. Quel est le problème alors?


Tout simplement pas sûr de la terminologie: j'utilise quelque chose comme ceci: f y [] = [] f y (x: xs) | x == 0 = ...... | Sinon = ...... serait-ce mieux en utilisant le motif correspondant à des gardes?


Sorte de pertinence, mais ne pose pas de pat motif sur les littéraux numériques descendants dans quelque chose comme des gardes de toute façon? Si je charge unités dans ghci avec noimplicitpelude actif, GHC se plaint de (==) ne pas être portée. Ou gère-t-il des motifs comme celui-là différemment parce que de noimplicitpelude ?


Si vous avez un ADT personnalisé et que vous souhaitez faire correspondre des constructeurs, la correspondance des motifs est la voie à suivre, elle s'applique également aux conditions simples (comme EQ). La correspondance des motifs ne fonctionnera pas sur des conditions plus complexes, c'est pourquoi vous avez des gardes, vous pouvez également utiliser la correspondance et les gardes combinés.


@CamCcan @Roman Gonzalez, donc essentiellement, je devrais utiliser le motif correspondant si cela fonctionne, non?


@camcann: Non, vous avez raison. Il descend de la correspondance des motifs. Le point clé est que l'utilisation de ce pragma, GHC prendra tout (==) , c'est dans la portée . Mais il n'y en a pas.



10
votes

Ma règle générale serait ceci:

  • Utilisez le motif correspondant lorsque la protection serait un simple == code> chèque. li> ul>

    avec récursion, vous vérifiez généralement un boîtier de base. Donc, si votre cas de base est un code == code>, puis utilisez la correspondance de modèle. P>

    donc je ferais généralement cela: p>

    f _ []      = []
    f 0 _       = ...
    f y (x:xs)  = ...
    


1 commentaires

Vient de mettre à jour ma question pour en rendre un peu plus clair. La première partie a raison, je suis au courant de cela. Juste un peu moins certain sur la deuxième partie (i.e. carte f (x: xs) et si f == 0 ?



2
votes

Il n'y a pas vraiment de règles difficiles et rapides à ce sujet, c'est pourquoi les réponses que vous avez obtenues étaient un peu floues. Certaines décisions sont faciles, comme la correspondance de modèle sur [] code> au lieu de la protection avec f xs | null xs = ... code> ou, heaven interdire, f xs | Longueur XS == 0 = ... code> terrible de plusieurs manières. Mais quand il n'y a pas de problème pratique convaincant, il suffit d'utiliser le code plus clair.

Par exemple, considérez ces fonctions (qui ne font pas vraiment quelque chose d'utile, servant simplement comme illustrations): P>

f1 _ [] = [] 
f1 0 (x:xs) = [[x], xs]
f1 y (x:xs) = [x] : f1 (y - 1) xs

f2 _ [] = []
f2 y (x:xs) | y == 0    = calc 1 : f2 (- x) xs
            | otherwise = calc (1 / y) : f2 (y * x) xs
  where calc z = x * ...


1 commentaires

+1 partagé est une bonne raison de préférer les gardes sur la correspondance des motifs. Notez également qu'en raison de la paresse, si vous entrez un garde qui n'utilise pas une partie du expressions, ces expressions non utilisées ne seront jamais évaluées (car elles n'ont pas besoin d'être).



2
votes

@Dan est correct: il s'agit essentiellement d'une question de préférences personnelles et n'affecte pas le code généré. Ce module: xxx

a produit le noyau suivant: xxx

exactement identique, à l'exception de la différence de cas par défaut, que dans les deux cas est une erreur de correspondance de modèle. GHC a même comméré les cordes pour les cas appariés.


0 commentaires

2
votes

Les réponses jusqu'à présent ne mentionnent pas l'avantage de la correspondance des motifs qui est la plus importante pour moi: la possibilité d'implémenter en toute sécurité les fonctions totales.

Lorsque vous faites correspondre la correspondance, vous pouvez accéder en toute sécurité à la structure interne de l'objet sans la peur. de cet objet étant autre chose. Si vous oubliez certains des motifs, le compilateur peut vous avertir (malheureusement cet avertissement est désactivé par défaut dans GHC). P>

Par exemple, lors de l'écriture: P>

-- compiles, crashes in runtime
map f xs | not (null xs)   = []
         | otherwise = f (head xs) : map f (tail xs)

-- does not have any way to compile
map f (h:t) = []
map f [] = f h : map f t


-- does not give any warnings
map f xs = f (head xs) : map f (tail xs)

-- can give a warning of non-exhaustive pattern match
map f (h:t) = f h : map f t


0 commentaires