Existe-t-il un moyen de savoir si une liste dans Haskell est infinie? La raison est que je ne souhaite pas appliquer des fonctions telles que length
à des listes infinies.
Réponses
Trop de publicités?En appliquant length
inconnu listes est généralement une mauvaise idée, tant pratiquement due à l'infini des listes, et sur le plan conceptuel, car souvent il s'avère que vous n'avez pas de soins sur la durée de toute façon.
Vous avez dit dans un commentaire:
Je suis très nouveau pour Haskell, de sorte que maintenant, ce n'est pas infinie de structures de rendre mes programmes très vulnérables?
Pas vraiment. Alors que certains d'entre nous souhaitent que il y avait de meilleures façons de distinguer entre nécessairement fini et nécessairement une infinité de données, vous êtes toujours en sécurité quand vous créez, processus, et d'examiner les paresseux structures de manière incrémentielle. Le calcul de la longueur est clairement pas progressif, mais la vérification pour voir si la longueur est au-dessus ou en dessous d'un certain seuil est, et très souvent c'est tout ce que tu voulais faire de toute façon!
Un cas trivial est un test pour les listes non vides. isNonEmpty xs == length xs > 0
est une mauvaise mise en œuvre, car il examine un nombre illimité d'éléments, lors de l'examen d'un seul devrait suffire! Comparer ceci:
isNonEmpty [] = False
isNonEmpty (_:_) = True
Ce n'est pas seulement est sûr de s'appliquer à une liste infinie, c'est aussi beaucoup plus efficace sur finis les listes--il ne faut que la constante de temps, plutôt que du temps linéaire en la longueur de la liste. C'est aussi la façon dont la bibliothèque standard de la fonction null
est mis en œuvre.
Pour généraliser cette pour la longueur de test par rapport à un cut-off, vous aurez évidemment besoin d'examiner autant de la liste, comme la longueur, comparé à l'. Nous pouvons faire exactement cela, et pas un de plus, à l'aide de la fonction de la bibliothèque standard drop
:
longerThan :: Int -> [a] -> Bool
longerThan n xs = isNonEmpty $ drop n xs
Donné une longueur d' n
et un (éventuellement infinie) de la liste xs
, cette baisse, le premier n
éléments xs
s'ils existent, puis vérifie si le résultat est non-vide. Parce qu' drop
produit de la liste vide si n
est plus grande que la longueur de la liste, cela fonctionne correctement pour tous positifs n
(hélas, il n'y a pas d'entier non négatif type, par exemple des nombres naturels, dans les bibliothèques standard).
Le point clé ici est que c'est mieux dans la plupart des cas à penser que les listes itératif flux, pas une simple structure de données. Si possible, vous voulez faire les choses comme transformer, d'accumuler, de les tronquer, etc., et de produire une autre liste de sortie ou de n'examiner qu'une quantité finie de la liste, plutôt que d'essayer de traiter l'ensemble de la liste en une seule fois.
Si vous utilisez cette approche, non seulement les fonctions fonctionnent correctement sur le fini et l' infini des listes à la fois, mais ils vont aussi bénéficier de plus de la paresse et de GHC l'optimiseur, et être susceptible de courir plus vite et d'utiliser moins de mémoire.
Le Problème de l'Arrêt a d'abord été révélée impossible à résoudre en supposant un Arrêt d'Oracle existé, puis écrire une fonction qui a fait tout le contraire de ce que l'oracle a dit qui allait arriver. Nous allons reproduire ici:
isInfinite :: [a] -> Bool
isInfinite ls = {- Magic! -}
Maintenant, nous voulons faire une liste impossibleList
qui fait le contraire de ce qu' isInfinite
dit qu'il devrait. Donc, si impossibleList
est infini, il est en fait []
, et si elle n'est pas infinie, c'est - something : impossibleList
.
-- using a string here so you can watch it explode in ghci
impossibleList :: [String]
impossibleList =
case isInfinite impossibleList of
True -> []
False -> "loop!" : impossibleList
Essayez vous-même dans ghci avec isInfinite = const True
et isInfinite = const False
.
Nous n'avons pas besoin de résoudre le Problème de l'Arrêt d'appel de la longueur en toute sécurité. Nous avons juste besoin d'être prudent; accepter tout ce qui a une finitude de la preuve, de rejeter tout ce qui n'est pas (y compris de nombreux finis les listes). C'est exactement ce type de systèmes sont pour, donc nous utilisons le type suivant (t est notre type d'élément, qui nous ignorer):
terminatingLength :: (Finite a) => a t -> Int
terminatingLength = length . toList
Les limites de la classe ne contient finis listes, de sorte que le type-checker va nous assurer d'avoir un fini argument. l'adhésion de Finis sera notre preuve de la finitude. Le "toList" fonction permet juste Fini de valeurs dans des Haskell listes:
class Finite a where
toList :: a t -> [t]
Maintenant, ce sont nos instances? Nous savons que le vide listes sont finis, donc, nous faisons un type de données pour les représenter:
-- Type-level version of "[]"
data Nil a = Nil
instance Finite Nil where
toList Nil = []
Si nous 'cons' un élément à une liste déterminée, nous obtenons une liste restreinte (par exemple "x:xs" est finie si "x" est finie):
-- Type-level version of ":"
data Cons v a = Cons a (v a)
-- A finite tail implies a finite Cons
instance (Finite a) => Finite (Cons a) where
toList (Cons h t) = h : toList t -- Simple tail recursion
Toute personne appelant notre terminatingLength fonction doit maintenant prouver que leur liste est finie, sinon le code ne compile pas. Cela n'a pas supprimé le Problème de l'Arrêt d'émission, mais nous avons décalé à la compilation plutôt qu'au moment de l'exécution. Le compilateur peut se bloquer lors de la tentative de déterminer l'appartenance de l'être Fini, mais c'est mieux que d'avoir un programme de production accrocher quand il est donné quelques données inattendues.
Un mot de prudence: Haskell 'ad-hoc' le polymorphisme permet d'assez bien arbitraire des instances de Finis pour être déclaré à d'autres endroits dans le code, et terminatingLength acceptera comme finitude preuves, même s'ils ne sont pas. Ce n'est pas trop mal, si quelqu'un essaye de contourner les mécanismes de sécurité de votre code, ils obtiennent les erreurs qu'ils méritent ;)
Non - vous pouvez au mieux estimer. Voir le problème de l' arrêt .