Domanda

Ho dei problemi a fare una sequenza. Fondamentalmente ho bisogno di tagliare una sequenza in una sequenza di matrici. Seq.windowed quasi lo fa, ma non voglio che gli elementi duplicati.

posso ottenere ciò che voglio leggendo tutto in un array prima ma io preferisco utilizzare una sequenza.

let array_chunk s (a:int[]) =
    Array.init (a.Length / s) (fun i -> Array.sub a (i * s) s)

someSequence |> Seq.to_array |> array_chunk 5
È stato utile?

Soluzione

Ecco una bella imperativo che lavorerò con numero di sequenza e generare matrici di qualsiasi dimensione. L'ultimo sarà più piccolo se la sequenza non è nemmeno da n.

let chunk n xs = seq {
    let i = ref 0
    let arr = ref <| Array.create n (Unchecked.defaultof<'a>)
    for x in xs do
        if !i = n then 
            yield !arr
            arr := Array.create n (Unchecked.defaultof<'a>)
            i := 0 
        (!arr).[!i] <- x
        i := !i + 1
    if !i <> 0 then
        yield (!arr).[0..!i-1] }

Altri suggerimenti

Amo Seq.take & Seq.skip soluzione. E 'bello, semplice e molto leggibile, ma vorrei usare qualcosa come questo:

let chunks n (sequence: seq<_>) =
    let fold_fce (i, s) value = 
        if i < n then (i+1, Seq.append s (Seq.singleton value))
                 else (  1, Seq.singleton value)
    in sequence
    |> Seq.scan (fold_fce) (0, Seq.empty)
    |> Seq.filter (fun (i,_) -> i = n)
    |> Seq.map (Seq.to_array << snd )

Non è il codice fondamentale e dovrebbe essere più efficiente rispetto alla soluzione che utilizza Seq.skip. D'altra parte, trim sequenza di ingresso alla lunghezza divisibile per n. Se questo comportamento è inaccettabile può essere fissato mediante semplice modifica:

let chunks n (sequence: seq<_>) =
    let fold_fce (i, s) value = 
        if i < n then (i+1, Seq.append s (Seq.singleton value))
                 else (  1, Seq.singleton value)
    in sequence
    |> Seq.map (Some)
    |> fun s -> Seq.init_finite (n-1) (fun _ -> None) |> Seq.append s
    |> Seq.scan (fold_fce) (0, Seq.empty)
    |> Seq.filter (fun (i,_) -> i = n) 
    |> Seq.map (Seq.to_array << (Seq.choose (id)) << snd )

Questa risposta sarà probabilmente ottenere sepolto, ma qui è il mio prendere sul problema:

let chunk n xs = 
    xs 
    |> Seq.mapi(fun i x -> i/n, x)
    |> Seq.groupBy fst
    |> Seq.map (fun (_, g) -> Seq.map snd g)

Pro:

  • Utilizza solo ss, non gli array
  • O (n) runtime. Non O (n ^ 2) come Seq.skip / prendere soluzioni
  • Seq.length non deve essere un multiplo di n
  • piccolo e facile da capire?

Contro:

  • probabilmente non così efficace come loop imperativi / mutabili

Come su:

let rec chunks n sq =
  if not (Seq.is_empty sq) then 
    seq {
      yield Seq.take n sq |> Seq.to_array
      yield! chunks n (Seq.skip n sq)
    }
  else
    Seq.empty

Si noti che questo richiede sq avere un numero di elementi che è divisibile per n (perché Seq.take e Seq.skip, a differenza asporto di LINQ e Skip metodi di estensione, richiedono che la sequenza contiene almeno n elementi). Inoltre, questo non è efficiente come esplicitamente usando l'enumeratore sarebbe, ma è più elegante.

Versione corretta di prendere / saltare risposta, come funzione di estensione. Dovrebbe funzionare per lunghezze irregolari. Non ci sono garanzie per le prestazioni anche se ...

 module Seq = 
    let rec chunks n (s:#seq<_>) =
        seq {
                 if Seq.length s <= n then
                    yield s
                 else
                    yield Seq.take n s
                    yield! chunks n (Seq.skip n s)           
            }

(codice preso dalla mia risposta qui )

Questo è bello e concisa:

let chunk size (arr : 'a array) =
    [| for a in 0 .. size .. arr.Length - size -> arr.[a..a + size - 1] |]

Tuttavia, questo lops fuori gli elementi ultimi (arr.Length formato%) nella matrice. È possibile risolvere questo afferrando gli elementi mancanti e usare Array.append:

let chunk2 size (arr : 'a array) = 
    let first = [| for a in 0 .. size .. arr.Length - size -> arr.[a..a + size - 1] |]
    let numberOfMissingElements = arr.Length - (first.Length * size)
    if numberOfMissingElements > 0 then
        let last = [| arr.[arr.Length - numberOfMissingElements..] |]
        Array.append first last
    else first

Ecco un altro approccio con alcuni pattern matching - sembra più simile a * .iter, e li ho sputando liste piuttosto che le matrici, poiché è così che io di solito come i miei dati

.
let sequence_in_lists_of_length_n_with_acc (s: seq<'a>) n acc = seq {
use e = s.GetEnumerator()
let rec next_with_acc acc =
  match e.MoveNext(), acc with
  | true, a when List.length a + 1 = n ->  
    yield (List.rev (e.Current :: a))
    next_with_acc []
  | true, _ -> next_with_acc (e.Current :: acc)
  | false, _ ->
    f(List.rev acc)
  ()
next_with_acc []

}

Sono i gusti di questa soluzione migliore. Esso genera una nuova sequenza dalla sequenza esistente (il che significa che non ha bisogno di attraversare l'intera sequenza per ottenere un risultato - che è fondamentale se si sta facendo qualcosa di simile di elaborazione dei log, in cui non è possibile chiamare le cose come lunghezza).

ho finito per scrivere un Post con maggiori dettagli su come sono arrivato qui.

module Seq =  

lasciare grouped_by_with_leftover_processing F (f2: 'una lista -> list <' a> opzione) (s: seq < 'a>) =     lasciare rec grouped_by_with_acc (f: 'a ->' una lista -> 'un'opzione lista * 'una lista) Acc (vale a dire: IEnumerator <' a>) =       ss {         se ie.MoveNext ()         poi           lasciate nextValue, avanzi = f ie.Current acc           se nextValue.IsSome poi cedere nextValue.Value           dare la precedenza! avanzi grouped_by_with_acc F ie         altro           lasciare rem = f2 acc           se rems.IsSome poi cede rems.Value       }     ss {       dare la precedenza! grouped_by_with_acc f [] (s.GetEnumerator ())     }

lasciare YieldReversedLeftovers (f: 'una lista) =     se f.IsEmpty     allora Nessuno     Alcuni altro (List.rev f)

lasciare grouped_by f s =     grouped_by_with_leftover_processing f YieldReversedLeftovers s

lasciare group_by_length_n n s =     lasciare grouping_function newValue acc =       lasciare newList = newValue :: acc       // Se abbiamo la giusta lunghezza, ritorno       // un Alcuni come primo valore. che ti       // essere ceduto dalla sequenza.       se List.length acc = n - 1       poi alcuni (List.rev newList), []       // Se non abbiamo la giusta lunghezza,       // utilizzare Nessuno (quindi niente sarà ceduto)       altro Nessuno, newList
    grouped_by grouping_function s

Le grandi sequenze non sono un problema:

ss {for i in 1..1000000000 -> i} |> Seq.group_by_length_n 3 ;; Val IT: ss = Seq [[1; 2; 3]; [4; 5; 6]; [7; 8; 9]; [10; 11; 12]; ...] >

Versione Nizza da Princess è stato corretto per ottenere la coda e trasformato in ss

let array_chunk size (arr : 'a array) = 
    let maxl = arr.Length - 1
    seq { for a in 0 .. size .. maxl -> arr.[a .. min (a + size - 1) maxl ] }

Che ne dite di questo:

let grouped n = 
   Seq.unfold(fun s -> if not (Seq.isEmpty s) then 
                           Some (Seq.take n s, Seq.skip n s) 
                       else None) 

E 'nella stessa vena di risposta di KVB.

qualche modo ricordo (link?) Che una sequenza non ricorda la posizione, in modo che successive introito / saltare non sarà ottimale.

Ecco la soluzione @ di KVB con l'Seq.skip / prendere limiti fissi. È piccolo, elegante e O (n).

let eSkip n s = System.Linq.Enumerable.Skip(s, n)

let rec seq_chunks n sq =
  if (Seq.isEmpty sq) 
  then Seq.empty
  else seq {
      yield Seq.truncate n sq
      yield! seq_chunks n (eSkip n sq)
 }

Qui è la mia versione di prendere un array come ingresso e uscita:

  let chunk chunkNumber (array : _ array) =
    let chunkSize = array.Length/chunkNumber
    let mutable startIndex = 0
    [|
        let n1 = array.Length % chunkNumber
        for i = 1 to n1 do
            yield Array.sub array startIndex (chunkSize+1)
            startIndex <- startIndex + chunkSize+1

        let n2 = chunkNumber - n1
        for i = 1 to n2 do
            yield Array.sub array startIndex chunkSize
            startIndex <- startIndex + chunkSize
    |]

La funzione cerca di fare pezzi di dimensioni simili (invece di ottenere una piccolissima ultimo blocco) e costruisce l'uscita allo stesso modo si potrebbe costruire una sequenza (rendendo più semplice per riscriverlo al fine di ottenere una sequenza come output)

Riassumendo quanto sopra o Chunking buffering o segmentazione di un seqence, una lista o matrice. Due forme:

let rec chunk size xs = seq {
    yield Seq.take size xs
    yield! chunk size (Seq.skip size xs)
    }

o

let chunk size = Seq.unfold (fun xs ->
    match Seq.isEmpty xs with
    | false -> Some(Seq.take size xs, Seq.skip size xs)
    | _ -> None
    )

Nota: se Seq funzionato correttamente come un cursore (come mi sarei aspettato di essere una valutazione pigra), Seq.take avrebbe avanzare il Seq.skip posizione non sarebbe necessario. Tuttavia, questo non è il caso.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top