F#List SelectMany
linq (4)
Esta es una pregunta bastante simple pero no encontré una respuesta:
¿Hay alguna operación Seq / List en F # para coincidir con LINQ SelectMany?
- Sé que puedo usar System.Linq en F # si quiero.
- Sé que puedo crear un método recursivo y usar F # Computation Expressions (y hacer cosas aún más poderosas).
Pero si intento probar que las operaciones de la Lista F # son más poderosas que LINQ ...
- .Where = List.filter
- .Seleccione = List.map
- .Aggregate = List.fold
- ...
En C # SelectMany la sintaxis de uso es bastante simple:
var flattenedList = from i in items1
from j in items2
select ...
¿Hay alguna coincidencia directa fácil, List.flatten, List.bind o algo así?
SelectMany tiene un par de firmas, pero la más compleja parece ser:
IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(
this IEnumerable<TSource> source,
Func<TSource, IEnumerable<TCollection>> collectionSelector,
Func<TSource, TCollection, TResult> resultSelector
);
En términos de F # esto sería:
(''a -> ''b list) -> (''a -> ''b -> ''c) -> ''a list -> ''c list
Otras publicaciones muestran cómo emparejar el linq con
A partir de este linq:
var flattenedList = from i in items1
from j in items2
select ...
var flattenedList2 = items1.SelectMany(i => items2.Map(j => ...))
Equivalente F # es:
let flattenedList = seq {
for a in items1 do
for b in items2 do
yield ... }
let flattenedList2 = items1 |> Seq.collect (fun i -> items2 |> Seq.map (fun j -> ...))
Los dos bits de código son aproximadamente equivalentes en expresividad y complejidad.
Dicho esto, tratemos un comentario específico en tu publicación:
Pero si intento probar que las operaciones de la Lista F # son más poderosas que LINQ ...
Las operaciones en los módulos Seq / List son aproximadamente equivalentes a las extensiones Enumerable / Linq.
Sin embargo, yo diría que la característica asesina para las listas es la capacidad de hacer coincidir patrones en ellas. Aquí hay un ejemplo tonto que no se convierte fácilmente a linq:
let rec funky = function
| x::y::z::rest -> (z, y)::funky(z::x::rest)
| [y;z]-> [(z, y)]
| [z] -> [(z, z)]
| [] -> []
// funky [1..6]
// = (int * int) list = [(3, 2); (4, 1); (5, 3); (6, 4)]
Esto sería un poco difícil de reimplementar en C #, pero es muy simple escribir F #.
Puedes usar List.collect o Seq.Collect:
let items1 = [1; 2; 3]
let items2 = [4; 5; 6]
let flat = items1 |> List.collect (fun i1 -> items2 |> List.map (fun i2 -> [i1, i2]))
Eso sería aproximadamente equivalente al siguiente código de C #:
var flat = from i1 in items1
from i2 in items2
select new { i1, i2 };
Seq.bind
es lo que quieres. SelectMany
es realmente un enlace monádico :).
Así que harías:
seq { for i in items1 do
for j in items2 do
yield .... };
collect
es el equivalente F # de SelectMany, sin embargo, no proporciona todas las sobrecargas. Aquí es cómo hacer el que se hace referencia.
let selectMany (ab:''a -> ''b seq) (abc:''a -> ''b -> ''c) input =
input |> Seq.collect (fun a -> ab a |> Seq.map (fun b -> abc a b))
// gives
// val selectMany : (''a -> seq<''b>) -> (''a -> ''b -> ''c) -> seq<''a> -> seq<''c>
Creo que F # no proporciona todas las sobrecargas de SelectMany porque agregarían ruido a la biblioteca. Aquí están las cuatro sobrecargas para SelectMany en Microsoft Naming.
let selectMany (source : ''TSource seq) (selector : ''TSource -> ''TResult seq) =
source |> Seq.collect selector
let selectMany (source : ''TSource seq) (selector : ''TSource -> int -> ''TResult seq) =
source |> Seq.mapi (fun n s -> selector s n) |> Seq.concat
let selectMany (source : ''TSource)
(collectionSelector : ''TSource -> ''TCollection seq)
(resultSelector : ''TSource -> ''TCollection -> ''TResult) =
source
|> Seq.collect (fun sourceItem ->
collectionSelector sourceItem
|> Seq.map (fun collection -> resultSelector sourceItem collection))
let selectMany (source : ''TSource)
(collectionSelector : ''TSource -> int -> ''TCollection seq)
(resultSelector : ''TSource -> ''TCollection -> ''TResult) =
source
|> Seq.mapi (fun n sourceItem ->
collectionSelector sourceItem n
|> Seq.map (fun collection -> resultSelector sourceItem collection))
|> Seq.concat
"Las operaciones de F # List son más poderosas que LINQ ..." Mientras que las operaciones de seq / list son excelentes, cierto "F # power" proviene de la Composición de funciones y el Currying .
// function composition
let collect selector = Seq.map selector >> Seq.concat