11
votes

F # séquence divisée en sous-listes sur chaque nième élément

dire que j'ai une séquence de 100 éléments. Chaque 10ème élément Je souhaite une nouvelle liste des 10 éléments précédents. Dans ce cas, je finirai avec une liste de 10 sublistes.

seq.take (10) semble prometteur, comment puis-je l'appeler à plusieurs reprises pour renvoyer une liste de listes?

f#

0 commentaires

8 Réponses :


10
votes

Ce n'est pas mauvais:

let splitEach n s =
    seq {
        let r = ResizeArray<_>()
        for x in s do
            r.Add(x)
            if r.Count = n then
                yield r.ToArray()
                r.Clear()
        if r.Count <> 0 then
            yield r.ToArray()
    }

let s = splitEach 5 [1..17]
for a in s do
    printfn "%A" a
(*
[|1; 2; 3; 4; 5|]
[|6; 7; 8; 9; 10|]
[|11; 12; 13; 14; 15|]
[|16; 17|]
*)


2 commentaires

En optimisation mineure, vous pourriez passer n dans le constructeur de resizearray, pour définir la capacité initiale à ce que vous savez que cela aura besoin de tenir.


Je vais avec cette réponse parce que c'est le plus facile pour moi de comprendre.



1
votes

Out du haut de ma tête:

let rec split size list =
if List.length list < size then
    [list]
else
    (list |> Seq.take size |> Seq.toList) :: (list |> Seq.skip size |> Seq.toList |> split size)


2 commentaires

Un problème possible avec cette approche est que chaque morceau énumère la séquence du début (jusqu'à la fin du morceau), il existe donc environ N / 2 fois plus d'itération que nécessaire (pour une séquence de longueur n).


Devez-vous convertir la séquence en une liste avant d'appeler cette fonction?



0
votes

Je pense que la solution de Brian est probablement l'option simple la plus raisonnable. Un probelm avec des séquences est qu'ils ne peuvent pas être facilement traités avec la correspondance habituelle des motifs (comme des listes fonctionnelles). Une option pour éviter que ce soit d'utiliser lazyliste code> de F # Powerpack.

Une autre option consiste à définir un constructeur de calcul pour travailler avec ienumerator code> type. J'ai récemment écrit quelque chose comme ça récemment - Vous pouvez l'obtenir ici . Ensuite, vous pouvez écrire quelque chose comme: P>

let splitEach chunkSize (s:seq<_>) =
  Enumerator.toSeq (fun () -> 
    let en = s.GetEnumerator()
    let rec loop n acc = iter {
      let! item = en
      match item with 
      | Some(item) when n = 1 -> 
          yield item::acc |> List.rev 
          yield! loop chunkSize []
      | Some(item) -> 
          yield! loop (n - 1) (item::acc)
      | None -> yield acc |> List.rev }
    loop chunkSize [] )


0 commentaires

2
votes

J'ai une évolution de trois solutions. Aucun d'entre eux ne conserve la commande des éléments d'entrée, espérons-le, d'accord.

Ma première solution est assez laide (utilisation de cellules refaf): p>

each [] [] 0 |> List.rev |> List.map List.rev


0 commentaires

1
votes

Peut-être que cette simple implémentation pure pourrait être utile: xxx

par exemple: xxx


0 commentaires

1
votes

En cas de doute, utilisez le pli.

let split n  = let one, append, empty = Seq.singleton, Seq.append, Seq.empty
               Seq.fold (fun (m, cur, acc) x -> 
                           if m = n then (1, one x, append acc (one cur))
                           else (m+1, append cur (one x), acc))
                        (0, empty, empty)
               >> fun (_, cur, acc) -> append acc (one cur)


0 commentaires

1
votes

J'ai trouvé que cela soit facilement le plus rapide:

let windowChunk n xs =
    let range = [0 .. Seq.length xs]
    Seq.windowed n xs |> Seq.zip range 
                      |> Seq.filter (fun d -> (fst d) % n = 0)
                      |> Seq.map(fun x -> (snd x))


0 commentaires

23
votes

maintenant il y a seq.chunkbysize disponible: xxx


0 commentaires