¿Cuándo usar una secuencia en F # en lugar de una lista?

82

Entiendo que una lista en realidad contiene valores y una secuencia es un alias de IEnumerable<T>. En el desarrollo práctico de F #, ¿cuándo debería usar una secuencia en lugar de una lista?

Aquí hay algunas razones por las que puedo ver cuándo sería mejor una secuencia:

  • Al interactuar con otros lenguajes .NET o bibliotecas que lo requieran IEnumerable<T>.
  • Necesita representar una secuencia infinita (probablemente no sea realmente útil en la práctica).
  • Necesita una evaluación perezosa.

¿Hay otros?

dodgy_coder
fuente
3
Encuentro las secuencias infinitas muy útiles y comunes. System.Random.Next () ya es una "secuencia infinita" disfrazada, por ejemplo. A menudo quiero algo que genere tantos elementos como sea necesario. Recientemente escribí un Tetris en F # y representé la generación de bloques como una secuencia infinita: creará tantos como sea necesario a medida que avanza el juego.
Asik
2
@Dr_Asik Tenga en cuenta que un seqgenerado de esa manera producirá diferentes números aleatorios cada vez que lo mire. Eso obviamente puede ser una fuente de errores no deterministas ...
JD

Respuestas:

96

Creo que tu resumen de cuándo elegir Seqes bastante bueno. Aquí hay algunos puntos adicionales:

  • Úselo Seqpor defecto al escribir funciones, porque entonces funcionan con cualquier colección .NET
  • Úselo Seqsi necesita funciones avanzadas como Seq.windowedoSeq.pairwise

Creo que elegir Seqpor defecto es la mejor opción, entonces, ¿cuándo elegiría un tipo diferente?

  • Úselo Listcuando necesite procesamiento recursivo usando los head::tailpatrones
    (para implementar algunas funciones que no están disponibles en la biblioteca estándar)

  • Úselo Listcuando necesite una estructura de datos simple e inmutable que pueda construir paso a paso
    (por ejemplo, si necesita procesar la lista en un hilo, para mostrar algunas estadísticas, y al mismo tiempo continuar construyendo la lista en otro hilo a medida que recibe más valores, es decir, de un servicio de red)

  • Úselo Listcuando trabaje con listas cortas: la lista es la mejor estructura de datos para usar si el valor a menudo representa una lista vacía , porque es muy eficiente en ese escenario

  • Úselo Arraycuando necesite grandes colecciones de tipos de valores
    (los arreglos almacenan datos en un bloque de memoria plano, por lo que son más eficientes en memoria en este caso)

  • Úselo Arraycuando necesite acceso aleatorio o más rendimiento (y localidad de caché)

Tomas Petricek
fuente
1
Muchas gracias, exactamente lo que buscaba. Es confuso aprender F # para averiguar por qué hay estos dos elementos (lista y secuencia) que le brindan una funcionalidad similar.
dodgy_coder
3
"Use List[...] cuando necesite una estructura de datos simple e inmutable que pueda construir paso a paso [...] y al mismo tiempo continuar construyendo la lista en otro hilo [...]" ¿Puede por favor ampliar su queriendo decir aquí / ¿cómo funciona? Gracias.
Narfanar
2
@Noein La idea es que siempre puede iterar sobre listas (son inmutables) pero puede crear nuevas listas usando x::xssin romper ningún trabajador existente que pueda estar en proceso de iteraciónxs
Tomas Petricek
29

También prefiera seqcuando:

  • No desea mantener todos los elementos en la memoria al mismo tiempo.

  • El rendimiento no es importante.

  • Debe hacer algo antes y después de la enumeración, por ejemplo, conectarse a una base de datos y cerrar la conexión.

  • No está concatenando (repetidamente Seq.appendse desbordará la pila).

Preferir listcuando:

  • Hay pocos elementos.

  • Estarás anteponiendo y decapitando mucho.

Ni seqtampoco listson buenos para el paralelismo, pero eso tampoco significa necesariamente que sean malos. Por ejemplo, puede utilizar cualquiera de los dos para representar un pequeño grupo de elementos de trabajo separados que se realizarán en paralelo.

JD
fuente
"Ni seq ni list son buenos para el paralelismo": ¿podría explicar por qué seq no es bueno para el paralelismo? Entonces, ¿qué es bueno para el paralelismo, solo matriz?
Asik
5
@Dr_Asik Las matrices son las mejores porque puede subdividirlas de forma recursiva y conservar una buena localidad de referencia. Los árboles son los siguientes mejores porque también puede subdividirlos, pero la localidad de referencia no es tan buena. Las listas y secuencias son malas porque no puede subdividirlas. Si agrupa elementos alternativos, obtendrá la peor localidad de referencia posible. Guy Steele ha discutido las colecciones lineales que impiden el paralelismo, aunque solo considera el trabajo y la profundidad y no la localidad (también conocida como complejidad de caché ). labs.oracle.com/projects/plrg/Publications/…
JD
12

Solo un pequeño punto: Seqy Arrayson mejores que Listpor paralelismo.

Tiene varias opciones: PSEQ de F # PowerPack, Array.Parallel módulo y Async.Parallel (computación asíncrona). List es terrible para la ejecución paralela debido a su naturaleza secuencial ( head::tailcomposición).

almohadilla
fuente
Ese es un buen punto: el escenario que tenía en mente era cuando necesita construir la colección en un hilo (es decir, cuando recibe valores de algún servicio) y usarlo desde otro hilo (es decir, para calcular estadísticas y mostrarlo). Estoy de acuerdo en que para el procesamiento en paralelo (cuando ya tienes todos los datos en la memoria), tienes Arrayo PSeqson mucho mejores.
Tomas Petricek
1
¿Por qué dices que seqes mejor que listpor paralelismo? seqtambién son horribles para la ejecución paralela debido a su naturaleza secuencial ...
JD
7

list es más funcional y compatible con las matemáticas. cuando cada elemento es igual, 2 listas son iguales.

la secuencia no lo es.

let list1 =  [1..3]
let list2 =  [1..3]
printfn "equal lists? %b" (list1=list2)

let seq1 = seq {1..3}
let seq2 = seq {1..3}
printfn "equal seqs? %b" (seq1=seq2)

ingrese la descripción de la imagen aquí

Rm558
fuente
5

Siempre debe exponer Seqen sus API públicas. Utilice Listy Arrayen sus implementaciones internas.

Aleš Roubíček
fuente
¿Es porque funcionan bien con otros lenguajes .NET? es decir, porque a Seqse ve como un IEnumerable<T>?
dodgy_coder
No, por buenas prácticas de diseño. Exponga tanta información como sea necesario, no más.
Aleš Roubíček
Ok comentario justo, esta es una buena práctica para el código C # también, es decir, una función podría definirse mejor como IEnumerable <T> en lugar de la List <T> más pesada, por ejemplo.
dodgy_coder
1
Estoy de acuerdo en los contextos de estructuras de retorno mutables (por ejemplo, este defecto de diseño en .NET: msdn.microsoft.com/en-us/library/afadtey7.aspx ) o API que pueden usarse desde otros lenguajes .NET, pero yo no de acuerdo en general, en parte porque seqson tan malos para el paralelismo.
JD