f# linq

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