11
votes

Manière la plus idiomatique d'écrire des lots de taille SEQ dans F #

J'essaie d'apprendre F # en réécrivant certains algorithmes C # je suis dans idiomatique F #.

L'une des premières fonctions que j'essaie de réécrire est un lots de latche où: p> xxx pré>

qui diviserait la séquence en lots avec un max de cinq dans chacun, c'est-à-dire : p> xxx pré>

Ma première tentative de faire est une sorte de moche où j'ai eu recours à un objet mutable ref fort> ref fort> Utilisez TYPE MAUTABLE STRAND> à l'intérieur de la fermeture. En utilisant ref fort> est particulièrement désagréable depuis la désagréférence, vous devez utiliser l'opérateur ! Strong> qui, lorsque, à l'intérieur d'une expression de la condition, peut être intuitif à certains devs qui le lira comme logique non fort>. Un autre problème que j'ai rencontré est l'endroit où seq.skip et seq.take ne sont pas comme leurs alias Linq en ce qu'ils jettent une erreur si Taille forte> dépasse la taille de la séquence. P>

let batchesOf size (sequence: _ seq) : _ list seq =
    seq {
        let s = ref sequence
        while not (!s |> Seq.isEmpty)  do
            yield !s |> Seq.truncate size |> List.ofSeq
            s := System.Linq.Enumerable.Skip(!s, size)
    }


3 commentaires

Related: Stackoverflow.com/questions/7044100/... Stackoverflow.com/Questtions/6736464/split-seq-in-f/...


Vaguement lié ​​à


Voir également l'opération "Chunking" en Deedle: bluemountaintacital.github.io/deedle/timesseries.html#windowi ng (via @tomas Petricek)


10 Réponses :


16
votes

Mise en œuvre de cette fonction à l'aide du type SEQ <_> Idiomatiquement est difficile - le type est intrinsèquement mutable, donc il n'y a pas de simple façon fonctionnelle. Votre version est assez inefficace, car elle utilise Ignorer à plusieurs reprises sur la séquence. Un meilleur l'option impérative serait d'utiliser getenumerator et simplement itérer sur des éléments utilisant ienumerator . Vous pouvez trouver diverses options impératives de cet extrait: http://fssnip.net/1o

si vous ' Reprendre F #, il est préférable d'essayer d'écrire la fonction à l'aide du type de liste F #. De cette façon, vous pouvez utiliser un style fonctionnel idiomatique. Ensuite, vous pouvez écrire lotsof à l'aide de la correspondance de motif avec la récursion et l'argument de l'accumulateur, comme celui-ci: xxx

comme note de bas de page, la version impérative peut être apportée un peu Noir en utilisant Expression de calcul pour travailler avec ienumerator , mais ce n'est pas standard et que c'est un tour assez avancé (par exemple, voir http://fssnip.net/37 ).


1 commentaires

Doux remerciements pour les explications détaillées et des liens vers les alternatives!



4
votes

Ceci peut être fait sans récursion si vous voulez

[0..20] 
    |> Seq.mapi (fun i elem -> (i/size),elem) 
    |> Seq.groupBy (fun (a,_) -> a)
    |> Seq.map (fun (_,se) -> se |> Seq.map (snd));;
val it : seq<seq<int>> =
  seq
    [seq [0; 1; 2; 3; ...]; seq [5; 6; 7; 8; ...]; seq [10; 11; 12; 13; ...];
     seq [15; 16; 17; 18; ...]; ...]


2 commentaires

En utilisant GroupBy est une jolie truc soie - je pense que c'est une excellente approche pour une implémentation initiale de prototype (que vous pouvez être faite rapidement et facilement). Je suppose que la performance sera un peu un problème cependant. La version récursive sera probablement un peu meilleure et les solutions impératives de l'extrait que je parlent plus rapidement. Mais la performance n'est souvent pas un problème lors du prototypage et des tests.


Notez que groupby ne fournit probablement aucune assurance qu'elle préserve l'ordre des éléments. J'ai été très surpris quand j'ai découvert que PSEQ fonctionne de manière non déterministe vos séquences ...



1
votes

Ce n'est peut-être pas idiomatique, mais cela fonctionne:

let batchesOf n l = 
    let _, _, temp', res' = List.fold (fun (i, n, temp, res) hd ->
                                           if i < n then
                                             (i + 1, n, hd :: temp, res)
                                           else
                                             (1, i, [hd], (List.rev temp) :: res)) 
                                       (0, n, [], []) l
    (List.rev temp') :: res' |> List.rev


2 commentaires

Sont les citations simples sur Temp 'et Res' une convention F #?


Non, je ne pense pas dans ce cas. Je n'ai tout simplement pas eu l'imagination de faire venir avec plus de noms descriptifs :-)



1
votes

Voici une implémentation simple pour les séquences: xxx


0 commentaires

10
votes

Un ami m'a demandé cela un peu de retour. Voici une réponse recyclée. Cela fonctionne et est pur: xxx

ou une version impure: xxx

Ceux-ci produisent un SEQ > . Si vous devez vraiment avoir un 'une liste de liste comme dans votre exemple, ajoutez simplement ... |> seq.map (list.ofseq) |> list.oFseq dans: xxx

espère que cela aide!


2 commentaires

+1 Pour écrire le code le plus élégant dans l'exemple supérieur. J'utiliserais cela par défaut, sauf indication claire que les lieux du code où il est appliqué est relativement performant, comparé à d'autres lieux de code et la différence de performance est réellement perceptible par l'utilisateur.


Oui, c'est pur, mais ce n'est pas paresseux. L'appel Groupby consomme avec impatience l'ensemble de la séquence d'intrants, qui conflit dans le but de lotter les articles en premier lieu.



0
votes

Vous pouvez résoudre votre tâche avec analogique de Clojure < Code> partition Fonction de la bibliothèque ci-dessous: xxx

utilisé comme partition 5 5 Il vous fournira recherché lots de 5 fonctionnalité: xxx

comme prime en jouant avec N et étape Vous pouvez l'utiliser pour la tranchée chevauchement des lots alias fenêtres coulissantes et même s'appliquent à des séquences infinies, comme ci-dessous: xxx

considère comme un prototype " > Comme il effectue de nombreuses évaluations redondantes sur la séquence source et ne sont pas susceptibles de s'adapter à des fins de production.


0 commentaires

0
votes

Cette version passe tous mes tests que je pourrais penser, y compris ceux de l'évaluation paresseux et une évaluation de la séquence unique: xxx

Je suis toujours assez nouveau à f # donc si je manque quelque chose - s'il vous manque quelque chose corrige-moi, ce sera grandement apprécié.


0 commentaires

1
votes

Ma méthode consiste à convertir la liste en une matrice et de chunking récursivement le tableau:

    let batchesOf (sz:int) lt = 
        let arr = List.toArray lt

        let rec bite curr =
            if (curr + sz - 1 ) >= arr.Length then
                [Array.toList arr.[ curr .. (arr.Length - 1)]]
            else
                let curr1 = curr + sz 
                (Array.toList (arr.[curr .. (curr + sz - 1)])) :: (bite curr1)   

        bite 0

    batchesOf 5 [1 .. 17]

    [[1; 2; 3; 4; 5]; [6; 7; 8; 9; 10]; [11; 12; 13; 14; 15]; [16; 17]]


0 commentaires

1
votes

J'ai trouvé que ceci comme une solution assez terres: xxx

Il fonctionne sur une séquence et produit une séquence. La séquence de sortie consiste en des listes de n éléments de la séquence d'entrée.


0 commentaires

4
votes

hurray, nous pouvons utiliser list.chunkbysize , seq.chunkbysize et array.chunkbysize dans F # 4, comme mentionné par Brad Collins et Scott Wlaschin .


0 commentaires