Comment définiriez-vous la carte et le filtre à l'aide de foldr en Haskell?
Je suis en train de faire un peu d'auto-étude sur les langages fonctionnels (actuellement à l'aide de Haskell). Je suis tombé sur un Haskell affectation qui exige la définition de la carte et le filtre en termes de foldr. Pour la vie de moi je ne suis pas entièrement comprendre comment aller à ce sujet.
Par exemple quand je définis une fonction map comme:
map' :: (a -> b) -> [a] -> [b]
map' f [] = []
map' f (x:xs) = foldr (\x xs -> (f x):xs) [] xs
Je ne sais pas pourquoi le premier élément de la liste est toujours ignoré. Ce qui signifie que:
map' (*2) [1,2,3,4]
résultats dans [4,6,8] au lieu de [2,4,6,8]
De même, mon filtre de la fonction:
filter' :: (a -> Bool) -> [a] -> [a]
filter' p [] = []
filter' p (x:xs) = foldr (\x xs -> if p x then x:xs else xs ) [] xs
quand exécuter en tant qu':
filter' even [2,3,4,5,6]
résultats dans [4,6] au lieu de [2,4,6]
Pourquoi serait-ce le cas? Et comment dois-je avoir défini ces fonctions pour obtenir les résultats attendus? Je suis en supposant que quelque chose est incorrect avec mes expressions lambda...
Vous devez vous connecter pour publier un commentaire.
Je souhaite que je pourrais juste un commentaire, mais hélas, je n'ai pas assez de karma.
Autres réponses sont toutes bonnes, mais je pense que la plus grande confusion semble être découlant de votre utilisation de x et xs.
Si vous réécrit comme
vous voir clairement que
x
n'est même pas mentionné sur le côté droit, donc il n'y a aucun moyen qu'il pourrait être dans la solution.Acclamations
map' f xs
au lieu demap' f (x:xs)
devrait résoudre le problème.-Wall
est utilisé:Warning: Defined but not used: 'x'
(ou, pour le code d'origine:This binding for 'x' shadows the existing binding
)map' f (x:xs)
parmap' f xs
comme suggéré par Dan BurtonPour votre première question,
foldr
a déjà un cas de la liste vide, de sorte que vous n'avez pas besoin et ne doit pas constituer un cas pour elle dans votre propre carte.La même chose vaut pour
filter'
Rien de mal avec vos expressions lambda, mais il ya quelque chose de mal avec votre définitions de
filter'
etmap'
. Dans les inconvénients cas (x:xs) vous mangez de la tête (x
), puis de passer à la queue defoldr
. Lefoldr
fonction peut ne jamais voir le premier élément que vous avez déjà mangé. 🙂Alse noter que:
est équivalent (η-équivalent) à:
Je définirais la carte à l'aide de foldr et fonction de la composition comme suit:
Et pour le cas de filtre:
Noter qu'il n'est pas nécessaire de passer à la liste elle-même lors de la définition des fonctions sur les listes à l'aide de foldr ou foldl.
Le problème avec ta solution est que vous déplacez la tête de la liste et d'appliquer ensuite la carte du dessus de la liste et
c'est pourquoi la tête de la liste est manquant lorsque le résultat est démontré.
filter p = foldr (\x -> if p x then (x:) else id) []
(:).f
plus facile à lire que\x xs -> f x : xs
?Data.Bool
et l'utilisationfilter p = foldr (bool id . (:) <*> p) []
. Aller vers les extrêmes,filter = (`foldr` []) . (bool id . (:) <*>)
.Dans vos définitions, vous faites la correspondance de modèle pour
x:xs
, ce qui signifie que, lorsque votre argument est[1,2,3,4]
,x
est lié à1
etxs
est lié au reste de la liste:[2,3,4]
.Ce que vous ne devriez pas faire est de simplement jeter
x:
partie. Ensuite, votrefoldr
sera de travailler sur la totalité de la liste.De sorte que votre définitions devraient se présenter comme suit:
et
[]
sont redondants ---foldr
fait la bonne chose quand vous passer[]
.Je suis nouveau sur Haskell (en fait, j'ai trouvé cette page de poser la même question), mais c'est ma compréhension de listes et de foldr jusqu'à présent:
(:)
de l'opérateur. ils se terminent avec la liste vide[]
. (pensez-y comme un opérateur binaire comme l'addition(+)
1+2+3+4 = 10
,1:2:3:4:[] = [1,2,3,4]
[]
. si vous avez un lien d'une liste vide de toute liste, le résultat est la liste elle-même. donc, pour unsum
la fonction est0
. pour multiplier la fonction est1
, etc.Si ma solution est la suivante:
l'expression lambda est notre fonction de lien, qui sera utilisé à la place de la contre
(:)
de l'opérateur. La liste vide est notre valeur par défaut pour la liste vide. Si le prédicat est satisfait d'un lien vers l'élément suivant à l'aide de(:)
que la normale, sinon nous n'avons simplement pas de lien du tout.ici, nous avons un lien
f x
à l'élément suivant au lieu de simplementx
, ce qui serait simplement dupliquer la liste.Notez également que vous n'avez pas besoin d'utiliser le pattern matching, puisque nous avons déjà dire foldr que faire en cas de la liste vide.
Je sais que cette question est vraiment vieux, mais je voulais juste répondre à ça de toute façon. J'espère qu'il n'est pas contre les règles.
Une manière différente de penser à elle - foldr existe parce que le récursive modèle est souvent utilisé:
De prendre le produit de nombres, voire de l'inverser une liste semble structurellement très similaire à la précédente fonction récursive:
La structure dans les exemples ci-dessus ne se distingue de la valeur initiale (
0
summa et[]
pour reverso) avec l'opérateur entre la première valeur et l'appel récursif (+
summa et(\q qs -> qs ++ [q])
pour reverso). Donc la fonction de structure pour les exemples ci-dessus peut être considérée commeDe voir que ce "générique" foo œuvres, nous pouvons maintenant réécrire reverso en utilisant foo et de le transmettre à l'opérateur, la valeur initiale, et la liste elle-même:
Donnons foo plus générique type de signature, de sorte que cela fonctionne pour d'autres problèmes se posent:
Maintenant, pour en revenir à votre question, nous pourrions écrire filtre comme suit:
Ce nouveau dispose d'une structure très similaire à la summa et reverso. Par conséquent, nous devrions être en mesure d'utiliser foo à la réécrire. Disons que nous voulons filtrer le même nombres de la liste [1,2,3,4]. Puis, de nouveau, nous passons foo l'opérateur (dans ce cas
filterLogic
), valeur initiale, et la liste elle-même.filterLogic
dans cet exemple prend unp
la fonction d'un prédicat, que nous aurons à définir pour l'appel:foo en Haskell est appelé foldr. Nous avons donc réécrit filtre à l'aide de foldr.
Donc, filtre peut être écrit avec foldr comme nous l'avons vu:
Comme pour carte, on pourrait aussi l'écrire comme
qui, par conséquent, peut être réécrite en utilisant foldr. Par exemple, pour multiplier chaque nombre dans une liste en deux:
Donc, carte peut être écrit avec foldr comme nous l'avons vu:
Votre solution fonctionne presque .)
Le problème, c'est que vous avez deux differend liaisons pour x dans les deux fonctions (à l'Intérieur de la patternmatching et à l'intérieur de votre expression lambda), donc vous perdez le premier Élément.
Cela devrait le truc :). Aussi: vous pouvez écrire vos fonctions pointfree style facilement.