6 votes

Existe-t-il un moyen sûr de convertir une collection en une séquence en F# ?

Bon après-midi à tous !

Je me suis amusé à trouver des moyens de convertir une collection .NET en une structure de données fonctionnelle. La meilleure solution que j'ai trouvée est de la convertir d'abord en seq et ensuite en ce que je veux.

Le problème est que cela semble rompre l'inférence de type, ce qui n'est évidemment pas sûr.

Exemple :

let a = new System.DirectoryServices.DirectorySearcher("<query>") in
let entries = a.FindAll ()
let entries_list = 
     let (entries_seq : seq<obj>) = Seq.cast entries_list in
     Seq.toList entries_Seq
in
entries_list (* list of AD objects found from query, has type obj *)

Pour faire quoi que ce soit d'utile avec entries_list, je devrais faire :

entries_list :?> SearchResult

Essayer de le généraliser à un seq<'a> échoue, puisque le compilateur exige toujours que je tape statiquement son énumérateur (ce qui est logique).

Existe-t-il un moyen d'éviter cela ? Je commence à penser qu'il s'agit d'une limitation de l'utilisation des structures de données .NET d'une manière fonctionnelle.

Désolé si c'est une question de novice ; je suis novice en F# et en programmation fonctionnelle en général (et j'adore ça !). Merci !

  • Carlos.

12voto

kvb Points 35490

Comme le dit Daniel, vous n'avez généralement pas besoin d'utiliser la fonction Seq.cast car la plupart des collections mettront déjà en œuvre la fonction générique seq<'t> interface. Cependant, il existe plusieurs types de collections .NET construits avant l'introduction des génériques dans .NET 2.0 qui n'implémentent que l'interface non générique IEnumerable interface. Le compilateur F# dispose en fait d'une logique particulière dans for des boucles appelées "extraction énumérable" pour faciliter un peu le travail sur ces types de collections. Par conséquent, si vous n'avez affaire qu'à un seul de ces types de collection (par exemple, vous travaillez avec DirectoryServices.SearchResultCollections beaucoup), il est probablement plus judicieux de créer une simple fonction d'aide :

let typedSearchResults (s:SearchResultCollection) =
    seq { for result in s -> result }

que vous pouvez alors utiliser à la place de Seq.cast pour ce type de collection particulier.

Si vous utilisez un grand nombre de collections différentes à l'ancienne dans le même projet, vous pouvez utiliser certaines fonctionnalités de F# pour créer une collection générique. Seq.cast alternative :

module Seq =
    let inline inferCast s = 
        // constrain ^t to have an Item indexed property (which we don't actually invoke)
        let _ = fun x -> (^t : (member Item : int -> ^v with get) (x, 0))
        let e = (^t : (member GetEnumerator : unit -> ^e) s)
        seq { while (^e : (member MoveNext : unit -> bool) e) do
                yield (^e : (member Current : obj) e) :?> ^v }

Vous pouvez maintenant utiliser Seq.inferCast au lieu de Seq.cast et le type d'élément correct sera déduit pour vous. Dans votre cas, c'est probablement excessif.

3voto

Daniel Points 29764

La plupart des collections .NET mettent en œuvre IEnumerable<T> (alias seq<'T> en F#), mais vous en rencontrerez occasionnellement une qui n'implémente que l'interface non-générique IEnumerable . SearchResultCollection est l'un de ces types. Vous pouvez utiliser Seq.cast pour convertir ces collections en seq<'T> ce qui permet de les utiliser avec les fonctions de l'interface utilisateur. Seq module.

open System.DirectoryServices

use searcher = new DirectorySearcher("<query>")
let entries = searcher.FindAll() |> Seq.cast<SearchResult>
let entries_list = Seq.toList entries

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X