2010-04-13 23 views
13

En Haskell, hay una función "tomar n lista" que devuelve los primeros n elementos de una lista. Por ejemplo, "sum (take 3 xs)" resume los primeros tres elementos en la lista xs. ¿F # tiene un equivalente? Esperaba que fuera una de las funciones de lista, pero no puedo encontrar nada que parezca coincidir.¿F # tiene un equivalente a la toma de Haskell?

Respuesta

15

Sí, se llama Seq.take. El uso parece ser idéntico al de Haskell: Seq.takerecuento de origen. Para usarlo en una lista, primero use List.toSeq. (Actualización: Al parecer, a partir de los comentarios, esto no es necesario.)

+0

Eso parece ser exactamente lo que estoy buscando. Probando ahora. :) – McMuttons

+13

En realidad, la toma de haskell actúa como Seq.truncate, no Seq.take. – sepp2k

+2

Como la lista hereda de IEnumerable, no es necesario convertirla a una secuencia primero a partir de lo que puedo ver. Usar Seq.take en una lista funcionó bien. – McMuttons

39

Para aclarar algunas cosas, la diferencia entre Seq.take y Seq.truncate (como ha señalado @ sepp2k) es que el segundo le dará una secuencia que devuelve como máximo el número de elementos que especificó (pero si la longitud de la secuencia es menor, le dará menos elementos).

la secuencia generada Seq.take función producirá una excepción si se intenta acceder a un elemento más allá de la longitud de la lista original (Tenga en cuenta que la función Seq.take no lanza la excepción de inmediato, ya que el resultado se genera pereza secuencia).

Además, no necesita convertir la lista a una secuencia explícitamente. Debajo de la cubierta, list<'a> es una clase .NET que hereda del tipo seq<'a>, que es una interfaz. El tipo seq<'a> es en realidad solo un alias de tipo para IEnumerable<'a>, por lo que es implementado por todas las demás colecciones (incluidas matrices, listas de variables, etc.). El siguiente código funciona bien:

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

Sin embargo, si usted desea conseguir un resultado de tipo list<int> que necesita para convertir la secuencia de nuevo a una lista (lista es porque un tipo más específico de una secuencia):

let resList = res |> List.ofSeq 

no estoy seguro de por qué F # bibliotecas no proporcionan List.take o List.truncate. Supongo que el objetivo era evitar volver a implementar todo el conjunto de funciones para todos los tipos de colecciones, por lo que aquellas donde la implementación de secuencias es lo suficientemente buena cuando se trabaja con un tipo de colección más específico solo están disponibles en el módulo Seq (pero eso es solo mi suposición ...)

+0

Buena aclaración. – McMuttons

+0

@McMuttons: ¡Gracias! No estaba seguro de si debería publicarlo o no, porque la mayoría de las cosas ya se mencionaron en los comentarios. ¡Me alegro de que se usen! –

1

Seq.take funciona, como otros ya han indicado, pero todas las operaciones Seq en una lista tienen un costo. En el caso de Seq.take, no es sorprendente, ya que la lista debe ser copiada.

Es más notable que, por ejemplo, Seq.concat en una lista lleva mucho más tiempo que List.concat. Supongo que eso implica que no solo accede a la lista como una secuencia cuando llama a una función Seq.xxx, sino también que la lista está copiada/convertida a Seq detrás de escena.

edición: La razón por la que señaló a la conclusión anterior, fue este banco usando F # interactivo:

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

En mi máquina, la versión List.length tarda unos 1,9 segundos, mientras que la versión Seq.length tarda aproximadamente 3.8 segundos (el tiempo más corto de algunas pruebas repetidas de las líneas de longitud solamente, excluyendo la línea de generación de listas).

+3

No creo que esto sea correcto. 'list <'t>' implementa la interfaz 'seq <'t>' por lo que no es necesaria ninguna conversión, ni existe ninguna razón para esperar que se realice una copia. Además, 'Seq.take' funciona de forma perezosa, mientras que' List.take' no puede, por lo que en una larga lista, las operaciones de secuencia serán casi con toda seguridad más rápidas si solo se necesita el frente de la secuencia resultante. Sin embargo, puede ser cierto que la implementación de una función 'List.take' mediante la coincidencia de patrones funcionaría mejor que la implementación' Seq.take' que enumera la lista si desea acceder con ansia a todos los elementos de la lista resultante. – kvb

+1

Mi conclusión sobre las conversiones que tuvieron lugar probablemente fue incorrecta, pero algo está sucediendo y lleva más tiempo. Edité mi publicación y agregué algunos tiempos. – Batibix

Cuestiones relacionadas