10
votes

Pourquoi Linq n'a-t-il pas la tête et la queue?

Je me trouve souvent vouloir utiliser des méthodes de tête et de queue sur ienumerables, qui n'existent pas dans le cadre de Linq. Bien que je puisse facilement écrire le mien, je me demande s'ils ont été délibérément laissé de côté. Par exemple, xxx

donc, quelle est la meilleure pratique à ce sujet avec Linq? Est le fait que je continue à trouver des utilisations pour cela un signe que je ne fais pas quelque chose recommandé? Sinon, pourquoi ce paradigme fonctionnel commun n'a-t-il pas été mis en œuvre dans Linq?


5 commentaires

Premier () et dernier () ou FirstArdefault ()


Livre d'Oliver Sturm Programmation fonctionnelle en C # A beaucoup de code source téléchargeable. Si je me rappelle correctement, cela contient également des méthodes d'extension pour la tête et la queue.


@GertaNold J'ai des implications d'entre eux, question est davantage de savoir si cela est considéré comme une meilleure pratique.


Je comprends, c'est pourquoi je ne considère pas cela une réponse :). Mais le livre élabore également sur l'utilisation pratique des techniques de programmation fonctionnelle et des restrictions spécifiques en C #. Cela pourrait vous amener plus loin. (Il y a quelque temps, je le lisez).


@ Servélijssen, la queue d'une liste n'est pas dernier () . La queue est la liste sans son premier élément. Voir ici , ici , ici et ici .


3 Réponses :


17
votes

Techniquement, votre tête serait First () et votre Queue serait .skip (1) . Mais peut-être que vous pouvez trouver une meilleure solution? Comme en utilisant .agregistré () sur votre iEnumerable?


4 commentaires

@Matthewwatson no, queue n'est pas le dernier élément.


@Matthewwatson Oui. La chose avec la tête / la queue est que vous n'appelez pas la tête avant queue, c'est plutôt un concept de diviser une liste. NVOIGT a raison de dire que le code de l'OP est équivalent à appeler d'abord / sauter (1), et ce n'est pas un bon exemple de l'utilisation de la tête / de la queue.


.skip (1) n'est pas techniquement identique à celui de queue car il évalue toujours le premier élément, puis le rejette, par ex. list.sélectionnez (mapper) .skip (1) exécutera mapper (liste ([0]))


Eh bien, tandis que .skip (1) évalue effectivement le premier élément, le fait que mapper (liste ([0])) est exécuté dans votre exemple est due à la ne pas le faire dans le bon ordre. Faire list.skip (1) .Sélectionnez (mapper) ne fonctionnera pas mapper sur le premier élément.



1
votes

Parce que le concept "Head & Tail" est utilisé dans la programmation fonctionnelle pour correspondance de modèle et appels récursifs. Puisque c # ne prend pas en charge la correspondance des motifs, il n'est pas nécessaire de mettre en œuvre des méthodes de tête () et de queue (). xxx

comme pour votre cas - vous devez utiliser agrégate méthode.


0 commentaires

8
votes

Compte tenu de l'interface de iEnumerable , les performances ne peuvent pas toujours être assurées.

Vous remarquez que la plupart des langages de programmation fonctionnelle mettent en œuvre la queue et la tête. Cependant, il convient de noter que ces langues agissent dans des constructions de mémoire.

iEnumerable n'a pas de telles contraintes, et donc on ne peut donc pas supposer que cela serait efficace.

Un modèle fonctionnel commun, par exemple, serait de travailler récursivement sur la tête d'une collection, puis de recueil sur la queue de l'appel ...

Si vous l'avez fait avec Cadre d'entité Par exemple, vous enverriez l'appel suivant (Meta) sur le serveur SQL, en boucle serré. xxx

qui serait très inefficace.

EDIT:

Venez y penser. Une autre raison, est que c # / vb.net ne prend pas en charge la récursion de la queue, et donc, ce modèle peut facilement causer un Stackoverflow .


6 commentaires

Cette réponse est incorrecte et non pertinente. Il ne devrait pas être accepté. Un ienumerable queue est facilement accessible par < Code> .skip (1) , et il n'y a pas de performance surprend si la structure est en mémoire ou non. IEnumerable Les classes sont conçues pour déplacer un élément à la fois, et c'est tout Skip (1) fait. Les performances surprises ne sont que lorsque vous devez compter un iEnumerable ou passer à un élément d'un "index" arbitraire.


@kdbanman Skip and Enumerator.getNext () sont des choses différentes.


Généralement, oui tu as raison. Le pire des cas pour Skip (n) est d'appeler GetNext () interne n fois. qui n'est pas nécessaire dans tous les cas , mais prétendons que c'est. Ce n'est toujours pas un problème ici même dans le pire des cas. Skip (1) renvoie la queue, ce qui est demandé, et Skip (1) ne fait qu'un seul appel différé à GetNext ().


ici est la réelle implémentation .NET du Ignorer <> Méthode d'extension sur iEnumerable . Il est facile de voir mon point. Skip (1) ne fait qu'un seul appel différé à MOVENNEXT (), qui est la version de retour de BOOL de GetNext ().


Attends une seconde. Dans quel sens c # / vb.net ne supporte pas la récursion de la queue? Il me manque peut-être de manquer quelque chose ici, mais l'optimisation de l'appel de la queue fait partie de .NET pendant un certain temps (peut-être depuis 1.0?). Pour citer David Broman: " Les compilateurs Source-> IL et IL-> Native comprennent l'optimisation des appels queue ". Je peux rappeler spécifiquement au moins une session de débogage où je pensais "Comment je suis arrivé à ce cadre de la pile de cette adresse de retour? Oh, ouais - Appel de queue élision")


La récursion de la queue @unbob avait fait une partie du CLR depuis longtemps. C'est juste que les compilateurs pour c # et vb.net ne le soutiennent pas (pas sûr maintenant, car je n'ai pas suivi .net pendant des années)