web-dev-qa-db-fra.com

Différences Haskell (:) et (++)

Je suis désolé pour une question comme celle-ci. Je ne suis pas trop sûr de la différence du : et ++ opérateur à Haskell.

x:y:[] = [x,y]  

aussi

[x] ++ [y] = [x,y]

quant à la fonction inverse qui a posé cette question pour moi,

reverse ::[a]->[a]
reverse [] = []
reverse (x:xs) = reverse(xs)++[x]

Pourquoi les éléments suivants ne fonctionnent-ils pas?

reversex ::[Int]->[Int]
reversex [] = []
reversex (x:xs) = reversex(xs):x:[]

donnant une erreur de type.

43
DarthVader

L'opérateur : Est connu sous le nom d'opérateur "cons" et est utilisé pour ajouter un élément head à une liste. Donc [] Est une liste et x:[] Ajoute x à la liste vide en créant la liste [x]. Si vous contre alors y:[x] Vous vous retrouvez avec la liste [y, x] Qui est la même que y:x:[].

L'opérateur ++ Est l'opérateur de concaténation de liste qui prend deux listes comme opérandes et les "combine" en une seule liste. Donc, si vous avez la liste [x] Et la liste [y], Vous pouvez les concaténer comme ceci: [x]++[y] Pour obtenir [x, y].

Notez que : Prend un élément et une liste tandis que ++ Prend deux listes.

Quant à votre code qui ne fonctionne pas.

reversex ::[Int]->[Int]
reversex [] = []
reversex (x:xs) = reversex(xs):x:[]

La fonction inverse est évaluée dans une liste. Puisque l'opérateur : Ne prend pas une liste comme premier argument, alors reverse(xs):x n'est pas valide. Mais reverse(xs)++[x] est valide.

71
Vincent Ramdhanie

: contient un élément dans une liste.

++ ajoute deux listes.

Le premier a le type

a -> [a] -> [a]

alors que ce dernier a le type

[a] -> [a] -> [a]
21
Brian

Concaténation avec (++)

Peut-être que je pense à approfondir cela, mais, si je comprends bien, si vous essayez de concaténer des listes en utilisant (++) Par exemple:

[1, 2, 3] ++ [4, 5]

(++) Doit parcourir la liste complète de gauche. Jetez un oeil au code de (++) le rend d'autant plus clair.

(++) :: [a] -> [a] -> [a]
(++) []     ys = ys
(++) (x:xs) ys = x : xs ++ ys

Ainsi, il serait souhaitable d'éviter d'utiliser (++), Car à chaque appel reverse(xs)++[x] la liste s'agrandit (ou diminue selon le point de vue. Quoi qu'il en soit, le programme doit simplement parcourir une autre liste à chaque appel)

Exemple:

Disons que j'implémente l'inverse comme proposé par concaténation.

reversex ::[Int]->[Int]
reversex [] = []
reversex (x:xs) = reversex(xs)++[x]

Inverser une liste [1, 2, 3, 4] ressemblerait un peu à ceci:

reversex [1, 2, 3, 4]
reversex [2, 3, 4]               ++ [1]
reversex [3, 4]           ++ [2] ++ [1]
reversex [4]       ++ [3] ++ [2] ++ [1]
reversex [] ++ [4] ++ [3] ++ [2] ++ [1]
         [] ++ [4] ++ [3] ++ [2] ++ [1]
         [4]       ++ [3] ++ [2] ++ [1]
         [4, 3]           ++ [2] ++ [1]
         [4, 3, 2]               ++ [1]
         [4, 3, 2, 1]

Récursion de queue en utilisant l'opérateur cons (:) !!!

Une méthode pour gérer les piles d'appels consiste à ajouter un accumulateur . (il n'est pas toujours possible d'ajouter simplement un accumulateur. Mais la plupart des fonctions récursives dont on traite sont récursif primitif et peuvent donc être transformées en fonctions récursives de queue .)

Avec l'aide de l'accumulateur, il est possible de faire fonctionner cet exemple, en utilisant l'opérateur contre (:). L'accumulateur - ys dans mon exemple - accumule le résultat actuel et est transmis en tant que paramètre. Grâce à l'accumulateur, nous sommes maintenant en mesure d'utiliser l'opérateur contre pour accumuler le résultat en ajoutant à chaque fois la tête de notre liste initiale.

reverse' :: (Ord a) => [a] -> [a] -> [a]
reverse' (x:xs) ys = reverse' xs (x:ys)
reverse' [] ys     = ys

Il y a une chose à noter ici.

L'accumulateur est un argument supplémentaire. Je ne sais pas si Haskell fournit des paramètres par défaut, mais dans ce cas, ce serait bien, car vous appelleriez toujours cette fonction avec une liste vide comme accumulateur comme ceci: reverse' [1, 2, 3, 4] []

Il y a beaucoup de littérature sur la récursivité de la queue et je suis sûr qu'il y a beaucoup de questions similaires sur StackExchange/StackOverflow. Veuillez me corriger si vous trouvez des erreurs.

Sincères amitiés,

EDIT 1 :

Will Ness a souligné quelques liens vers de très bonnes réponses pour ceux d'entre vous qui sont intéressés:

EDIT 2 :

D'accord. Grâce à dFeuer et à ses corrections, je pense que je comprends un peu mieux Haskell.

1.Le $! Est au-delà de ma compréhension. Dans tous mes tests, cela a semblé empirer les choses.

Comme l'a souligné dFeuer: le thunk représentant l'application de (:) À x et y est sémantiquement identique à x:y Mais prend plus de mémoire. C'est donc spécial pour l'opérateur contre (et les constructeurs paresseux) et il n'est pas nécessaire de forcer les choses de quelque façon que ce soit.

3.Si je résume plutôt les entiers d'une liste en utilisant une fonction très similaire, une évaluation stricte via BangPatterns ou la fonction seq empêchera la pile de devenir trop grande si elle est utilisée de manière appropriée. par exemple.:

sumUp' :: (Num a, Ord a) => [a] -> a -> a
sumUp' (x:xs) !y = reverse' xs (x + y)
sumUp' [] y      = y

Remarquez le coup devant y. Je l'ai essayé en ghci et cela prend moins de mémoire.

12
Nima Mousavi

cons tend à être un constructeur de type qu'un opérateur. l'exemple ici est : peut être utilisé dans let..in.. expression mais ++ n'est pas

let x : xs = [1, 2, 3] in x -- known as type deconstructing

renverra 1 mais

let [x] ++ [y, z] = [1, 2, 3] in x

renverra une erreur Variable not in scope x

Pour simplifier, pensez à cons comme ceci

data List a = Cons a (List a) -- is equvalent with `data [a] = a:[a]`

https://en.wikibooks.org/wiki/Haskell/Other_data_structures

De plus, si vous souhaitez inverser un tableau en utilisant contre. Voici un exemple, la connaissance est tirée de Prolog

import Data.Function

reversex1 [] = []
reversex1 arr = reversex arr []

reversex [] arr = arr
reversex (x:xs) ys = reversex xs (x:ys)

main = do
    reversex1 [1..10] & print
3
Clite Tailor