les inconvénients de l'opérateur (::) en F#
L'opérateur :: en F#, toujours ajoute des éléments à la liste. Est-il un opérateur qui s'ajoute à la liste? Je suppose que l'utilisation d'opérateur @
[1; 2; 3] @ [4]
serait moins efficace, que d'ajouter un élément.
Vous devez vous connecter pour publier un commentaire.
Comme les autres ont dit, il n'existe pas d'opérateur, car il ne serait pas beaucoup de sens. En fait, je pense que c'est une bonne chose, car il est plus facile à réaliser que l'opération ne sera pas efficace. Dans la pratique, vous ne devriez pas avoir besoin de l'opérateur, il est généralement une meilleure façon d'écrire la même chose.
Scénario typique: je pense que le scénario typique où vous pourriez penser que vous avez besoin pour ajouter des éléments à la fin est si commun qu'il peut être utile de le décrire.
Ajoutant des éléments à la fin semble nécessaire lorsque vous êtes en train de rédiger une queue-une version récursive de la fonction à l'aide de la accumulateur paramètre. Par exemple, un (inefficace) de la mise en œuvre de
filter
fonction pour les listes ressemblerait à ceci:Dans chaque étape, nous avons besoin d'ajouter un élément à l'accumulateur (qui contient les éléments à être retournées comme résultat). Ce code peut être facilement modifié pour utiliser la
::
opérateur au lieu d'ajouter des éléments à la fin de laacc
liste:Dans (2), nous sommes maintenant en ajoutant des éléments à l'avant de l'accumulateur et lorsque la fonction est sur le point de retourner le résultat, nous avons inversé la liste (1), qui est beaucoup plus efficace que d'ajouter des éléments un par un.
list
type en F# est une liste doublement chaînée, donc l'inversion de l'opération, c'est juste pour mettre à jour certains indicateurs internes.Listes de F# sont liée individuellement et immuable. Cela signifie consing sur le front de l'est O(1) (créer un élément et l'ont point à une liste existante), alors que snocing sur le dos est en O(N) (comme l'ensemble de la liste doivent être répliqués; vous ne pouvez pas changer la finale pointeur, vous devez créer une nouvelle liste).
Si vous avez besoin d'ajouter un élément à l'arrière", puis, par exemple,
est la façon de le faire, mais c'est une odeur de code.
"snoc" == "cons".reverse
1@[42]
ne compile pas. peut-être1::[42]
est ce que vous recherchez?Le coût de l'ajout de deux listes standard est proportionnelle à la longueur de la liste sur la gauche. En particulier, le coût de
est proportionnelle à la longueur de
xs
—il est pas un coût constant.Si vous voulez une liste-comme l'abstraction avec une constante de temps append, vous pouvez utiliser de John Hughes est fonction de la représentation, je vais l'appeler
hlist
. Je vais essayer d'utiliser la syntaxe d'OCaml, qui, je l'espère, est assez proche de F#:L'idée est que vous représentez une liste fonctionnellement comme une fonction de "le reste des éléments" à "la liste finale". Cela fonctionne de la créer si vous allez mettre en place l'ensemble de la liste avant que vous regardez l'un quelconque des éléments. Sinon, vous allez avoir à traiter avec le linéaire des coûts d'ajouter ou d'en utiliser une autre structure de données entièrement.
Si elle l'est, ce sera un effet négligeable. À la fois l'ajout d'un seul élément et de la concaténation d'une liste à la fin sont
O(n)
opérations. Comme une question de fait, je ne peux pas penser à une seule chose que@
a à faire, qui d'un seul élément ajout de la fonction ne serait pas.[1,2,3,4]
est[4]
. Et vous ne pouvez pas construire une liste sans la construction de sa queue.a :: b :: c :: d :: []
. Pour ajouter un nouvel élément à la fin, vous avez à construirea :: b :: c :: d :: e :: []
. Et comme cela a été illustré précédemment,e :: []
est la même chose que[e]
. D'autre part, d'ajouter un élément à une liste, c'est justee :: original_list
.O(N^2)
. Ajout de N fois estO(N^2)
. Ajoutant à une liste une fois n'estO(N)
qui est la même que la concaténation d'une liste de longueur 1 à la fin de la liste. Oui, les préfixant est plus rapide (car il estO(1)
), mais ce n'était PAS la question. La question était de savoir si la concaténation d'un seul élément de la liste à la fin a été plus lent que l'ajout d'un seul élément. Qui elle ne l'est pas.@
avec un seul élément de la liste. Qui elle ne l'est pas.Peut-être vous voulez utiliser une autre structure de données. Nous avons une double-fini les files d'attente (ou court "Deques") dans fsharpx. Vous pouvez en lire plus sur eux à la http://jackfoxy.com/double-ended-queues-for-fsharp
L'efficacité (ou l'absence) vient d'itérer sur la liste pour trouver l'élément final. Si la déclaration d'une nouvelle liste avec
[4]
va être négligeable pour tous, mais le plus trivial des scénarios.Essayez d'utiliser un double-clos de la file d'attente au lieu de la liste. J'ai récemment ajouté 4 versions de deques (Okasaki de l'orthographe) à FSharpx.De base (Disponibles via NuGet. Le code Source à FSharpx.De base.Structures de données). Voir mon article sur l'utilisation de dequeus Double-fini les files d'attente pour F#
Je l'ai suggéré à l'équipe F# l'opérateur cons, ::, et de la configuration active discriminateur être mis à disposition pour d'autres structures de données avec une tête/queue de signature.Trois