2011-01-13 24 views
7

Sto cercando un modo per creare una sequenza composta da ogni ennesimo elemento di un'altra sequenza, ma non sembra trovare un modo per farlo in modo elegante. Ovviamente posso modificare qualcosa, ma mi chiedo se esiste una funzione di libreria che non vedo.Ottenere ogni ennesimo elemento di una sequenza

Le funzioni di sequenza i cui nomi finiscono in -i sembrano essere abbastanza buona con lo scopo di capire quando un elemento è l'ennesimo o (multiplo di n) esimo uno, ma posso vedere solo iteri e mapi, nessuno di cui si presta davvero al compito.

Esempio:

let someseq = [1;2;3;4;5;6] 
let partial = Seq.magicfunction 3 someseq 

Poi partial dovrebbe essere [3;6]. C'è qualcosa di simile là fuori?

Edit:

Se io non sono abbastanza ambiziosi e consentire la n essere costante/conosciuto, poi ho appena trovato che il seguente dovrebbe funzionare:

let rec thirds lst = 
    match lst with 
    | _::_::x::t -> x::thirds t // corrected after Tomas' comment 
    | _ -> [] 

Would ci sarà un modo per scrivere questo più breve?

+3

Puoi usare 'mapi' per trasformare ogni elemento della lista in un' Some' o un 'None',' filter' fuori 'None's, e poi 'mappa' di nuovo sul tipo non decorato. –

+0

La tua soluzione usando le liste sembra buona (ma probabilmente vuoi scrivere '_ :: _ :: x :: t' (invece di' (_, _, x) :: t' che usa la lista di tuple). è che 'Seq' funzionerà con altre raccolte rispetto alle liste, ma potrebbe non essere un problema per te. La tua versione con liste è un bel codice funzionale. –

+0

Sì, certo, deve essere' _ :: _ :: x :: t', avrebbe dovuto chiedere al compilatore prima di incollarlo qui. –

risposta

8

È possibile ottenere il comportamento componendo mapi con le altre funzioni:

let everyNth n seq = 
    seq |> Seq.mapi (fun i el -> el, i)    // Add index to element 
     |> Seq.filter (fun (el, i) -> i % n = n - 1) // Take every nth element 
     |> Seq.map fst        // Drop index from the result 

la soluzione con opzioni e choose come suggerito da Annone userebbe solo due funzioni, ma il corpo del primo sarebbe un po 'più complicato (ma il principio è essenzialmente lo stesso).

una versione più efficiente utilizzando direttamente l'oggetto IEnumerator non è troppo difficile da scrivere:

let everyNth n (input:seq<_>) = 
    seq { use en = input.GetEnumerator() 
     // Call MoveNext at most 'n' times (or return false earlier) 
     let rec nextN n = 
      if n = 0 then true 
      else en.MoveNext() && (nextN (n - 1)) 
     // While we can move n elements forward... 
     while nextN n do 
      // Retrun each nth element 
      yield en.Current } 

EDIT: Il frammento è anche disponibile qui: http://fssnip.net/1R

+0

Non so chi è stato più veloce, tu o "Anon", ma è lo stesso suggerimento, non è vero? Non sembra orribilmente efficiente, però, vero? –

+0

Non è orribilmente efficiente (ci sono alcune chiamate di funzione aggiuntive e riferimenti indiretti, perché usa 3 iteratori sotto la copertina), ma potrebbe non essere un problema (non ci sono liste intermedie che dovrebbero essere assegnate). Per una versione più efficiente, avrai bisogno di una mutazione (nell'espressione di sequenza) o userai il 'IEnumerator 'sottostante –

9

Seq.choose funziona bene in queste situazioni, perché consente di eseguire il lavoro filter all'interno del lambda mapi.

let everyNth n elements = 
    elements 
    |> Seq.mapi (fun i e -> if i % n = n - 1 then Some(e) else None) 
    |> Seq.choose id 

Simile a here.

+0

Bello ed elegante! –

+0

Sì, mi piace anche a me! :) –

Problemi correlati