El módulo F#Seq implementado en C#para IEnumerable?
(6)
F # tiene un grupo de operadores de secuencia estándar que he llegado a conocer y amar desde mi experiencia con Mathematica. F # está recibiendo mucha atención ahora, y cuando está en lanzamiento general, tengo la intención de usarlo con frecuencia.
En este momento, dado que F # aún no está en su versión general, realmente no puedo usarlo en el código de producción. LINQ implementa algunos de estos operadores utilizando nombres similares a SQL (por ejemplo, ''select'' es ''map'' y ''where'' es ''filter''), pero no puedo encontrar la implementación de ''fold'', ''iter'' o ''partition''.
¿Alguien ha visto alguna implementación C # de operadores de secuencia estándar? ¿Es esto algo que alguien debería escribir?
iter existe como un método en la clase List que es ForEach
de lo contrario:
public static void iter<T>(this IEnumerable<T> source, Action<T> act)
{
foreach (var item in source)
{
act(item);
}
}
- fold =
Aggregate
Dile a usar lo que hacen iter
y partition
, y podríamos completar los espacios en blanco. Supongo que iter = SelectMany
y la partición podría implicar Skip
/ Take
?
(actualización) Busqué Partition - aquí hay una implementación cruda que hace algo de esto:
using System;
using System.Collections.Generic;
static class Program { // formatted for space
// usage
static void Main() {
int[] data = { 1, 2, 3, 4, 5, 6 };
var qry = data.Partition(2);
foreach (var grp in qry) {
Console.WriteLine("---");
foreach (var item in grp) {
Console.WriteLine(item);
}
}
}
static IEnumerable<IEnumerable<T>> Partition<T>(
this IEnumerable<T> source, int size) {
int count = 0;
T[] group = null; // use arrays as buffer
foreach (T item in source) {
if (group == null) group = new T[size];
group[count++] = item;
if (count == size) {
yield return group;
group = null;
count = 0;
}
}
if (count > 0) {
Array.Resize(ref group, count);
yield return group;
}
}
}
Si observa cuidadosamente, muchas operaciones Seq tienen un equivalente LINQ o pueden derivarse fácilmente. Solo mirando la lista ...
Seq.append = Concat<TSource>(IEnumerable<TSource> second)
Seq.concat = SelectMany<IEnumerable<TSource>, TResult>(s => s)
Seq.distinct_by = GroupBy(keySelector).Select(g => g.First())
Seq.exists = Any<TSource>(Func<TSource, bool> predicate)
Seq.mapi = Select<TSource, TResult>(Func<TSource, Int32, TResult> selector)
Seq.fold = Aggregate<TSource, TAccumulate>(TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func)
List.partition
se define así:
Divida la colección en dos colecciones, que contengan los elementos para los cuales el predicado dado devuelve
true
yfalse
respectivamente
Que podemos implementar usando GroupBy y una matriz de dos elementos como tupla de un pobre:
public static IEnumerable<TSource>[] Partition<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
return source.GroupBy(predicate).OrderByDescending(g => g.Key).ToArray();
}
El elemento 0 contiene los valores verdaderos; 1 contiene los valores falsos. GroupBy es esencialmente partición en esteroides.
Y finalmente, Seq.iter
y Seq.iteri
trazan fácilmente foreach:
public static void Iter<TSource>(this IEnumerable<TSource> source, Action<TSource> action)
{
foreach (var item in source)
action(item);
}
public static void IterI<TSource>(this IEnumerable<TSource> source, Action<Int32, TSource> action)
{
int i = 0;
foreach (var item in source)
action(i++, item);
}
ToLookup probablemente sea una mejor coincidencia para List.partition:
IEnumerable<T> sequence = SomeSequence();
ILookup<bool, T> lookup = sequence.ToLookup(x => SomeCondition(x));
IEnumerable<T> trueValues = lookup[true];
IEnumerable<T> falseValues = lookup[false];
Aquí hay una actualización de la solución de partition
de dahlbyk .
Devolvió una array[]
donde "el elemento 0 contiene los valores verdaderos; 1 contiene los valores falsos" - pero esto no se cumple cuando todos los elementos coinciden o todos fallan el predicado, en cuyo caso usted tiene una matriz singleton y un mundo de dolor
public static Tuple<IEnumerable<T>, IEnumerable<T>> Partition<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
var partition = source.GroupBy(predicate);
IEnumerable<T> matches = partition.FirstOrDefault(g => g.Key) ?? Enumerable.Empty<T>();
IEnumerable<T> rejects = partition.FirstOrDefault(g => !g.Key) ?? Enumerable.Empty<T>();
return Tuple.Create(matches, rejects);
}
Rodar uno mismo en C # es un ejercicio interesante, aquí hay algunos de los míos. (Ver también aquí )
Tenga en cuenta que iter / foreach en un IEnumerable es un poco controvertido, creo que porque tiene que ''finalizar'' (o lo que sea la palabra) el IEnumerable para que realmente suceda algo.
//mimic fsharp map function (it''s select in c#)
public static IEnumerable<TResult> Map<T, TResult>(this IEnumerable<T> input, Func<T, TResult> func)
{
foreach (T val in input)
yield return func(val);
}
//mimic fsharp mapi function (doens''t exist in C#, I think)
public static IEnumerable<TResult> MapI<T, TResult>(this IEnumerable<T> input, Func<int, T, TResult> func)
{
int i = 0;
foreach (T val in input)
{
yield return func(i, val);
i++;
}
}
//mimic fsharp fold function (it''s Aggregate in c#)
public static TResult Fold<T, TResult>(this IEnumerable<T> input, Func<T, TResult, TResult> func, TResult seed)
{
TResult ret = seed;
foreach (T val in input)
ret = func(val, ret);
return ret;
}
//mimic fsharp foldi function (doens''t exist in C#, I think)
public static TResult FoldI<T, TResult>(this IEnumerable<T> input, Func<int, T, TResult, TResult> func, TResult seed)
{
int i = 0;
TResult ret = seed;
foreach (T val in input)
{
ret = func(i, val, ret);
i++;
}
return ret;
}
//mimic fsharp iter function
public static void Iter<T>(this IEnumerable<T> input, Action<T> action)
{
input.ToList().ForEach(action);
}