2010-04-13 13 views
13

In Haskell, esiste una funzione "take n list" che restituisce i primi n elementi da un elenco. Ad esempio "sum (take 3 xs)" riassume i primi tre elementi nell'elenco xs. F # ha un equivalente? Mi aspettavo che fosse una delle funzioni di elenco, ma non riesco a individuare nulla che sembra corrispondere.F # ha un equivalente di Take di Haskell?

risposta

15

Sì, si chiama Seq.take. L'utilizzo sembra essere identico a quello di Haskell: Seq.takecount source. Per utilizzarlo su un elenco, utilizzare prima List.toSeq. (Aggiornamento: A quanto pare dai commenti, questo non è necessario.)

+0

Questo sembra essere esattamente quello che sto cercando. Testare ora. :) – McMuttons

+13

In realtà, il take di haskell agisce come Seq.truncate, non Seq.take. – sepp2k

+2

Poiché l'elenco eredita da IEnumerable, non è necessario convertirlo prima in una sequenza da quello che posso vedere. Usare Seq.take in una lista ha funzionato bene. – McMuttons

39

Per chiarire un paio di cose, la differenza tra Seq.take e Seq.truncate (come sottolineato da @ sepp2k) è che la seconda vi darà una sequenza di che restituisce al massimo il numero di elementi specificati (ma se la lunghezza della sequenza è inferiore, fornirà meno elementi).

La sequenza generata Seq.take funzione un'eccezione se si tenta di accedere a un elemento al di là della lunghezza della lista originale (Si noti che la funzione Seq.take non genera immediatamente l'eccezione, perché il risultato è pigramente generato sequenza).

Inoltre, non è necessario convertire esplicitamente la lista in una sequenza. Sotto la copertina, list<'a> è una classe .NET che eredita dal tipo seq<'a>, che è un'interfaccia. Il tipo seq<'a> è in realtà solo un alias di tipo per IEnumerable<'a>, quindi è implementato da tutte le altre raccolte (inclusi array, elenchi modificabili, ecc.). Il seguente codice funziona bene:

let list = [ 1 .. 10 ] 
let res = list |> Seq.take 5 

Tuttavia, se si vuole ottenere un risultato di tipo list<int> avrete bisogno di convertire la sequenza di nuovo a una lista (perché l'elenco è di tipo più specifico di una sequenza):

let resList = res |> List.ofSeq 

io non sono sicuro perché F # librerie non forniscono List.take o List.truncate. Immagino che l'obiettivo fosse evitare di reimplementare l'intero set di funzioni per tutti i tipi di collezioni, quindi quelle in cui l'implementazione per le sequenze è abbastanza buona quando si lavora con un tipo di raccolta più specifico sono disponibili solo nel modulo Seq (ma questa è solo la mia ipotesi ...)

+0

Bel chiarimento. – McMuttons

+0

@McMuttons: Grazie! Non ero sicuro di doverlo postare o meno, perché la maggior parte delle cose erano già menzionate nei commenti .. Quindi, sono contento che sia usato! –

1

Seq.take funziona, come altri hanno già detto, ma tutte le operazioni Seq su una lista hanno un costo. Nel caso di Seq.take, non è sorprendente, poiché l'elenco deve essere copiato.

È più interessante notare che, ad esempio, Seq.concat in un elenco richiede molto più tempo di List.concat. Suppongo che ciò implichi che non si accede alla lista come seq quando si chiama una funzione Seq.xxx, ma che l'elenco viene copiato/convertito in Seq anche dietro le quinte.

edit: La ragione per cui ho tratto la conclusione di cui sopra, è stato questo panchina con F # interattivo:

#time "on";; 
let lists = [for i in 0..5000000 -> [i..i+1]];; 
Seq.length (Seq.concat lists);; 
List.length (List.concat lists);; 

Sulla mia macchina, la versione List.length dura circa 1,9 secondi, mentre la versione Seq.length dura circa 3.8 secondi (il tempo più breve di alcuni test ripetuti delle sole linee di lunghezza, esclusa la riga di generazione dell'elenco).

+3

Non penso che sia corretto. 'lista <'t>' implementa l'interfaccia 'seq <'t>' in modo che non sia necessaria alcuna conversione, né vi è motivo di aspettarsi che una copia venga eseguita. Inoltre, 'Seq.take' funziona pigramente, mentre' List.take' non può, quindi su una lunga lista, le operazioni di sequenza saranno quasi certamente più veloci se è necessario solo il fronte della sequenza risultante. Tuttavia, potrebbe essere vero che l'implementazione di una funzione 'List.take' tramite la corrispondenza dei pattern avrebbe prestazioni migliori rispetto all'implementazione' Seq.take' che enumera l'elenco se si desidera accedere con impazienza a tutti gli elementi della lista risultante. – kvb

+1

La mia conclusione sulle conversioni in corso è probabilmente sbagliata, ma qualcosa sta succedendo e ci vuole più tempo. Ho modificato il mio post e aggiunto alcuni tempi. – Batibix