2009-01-04 12 views
11

F # ma grupę standardowych operatorów sekwencyjnych, które poznałem i pokochałem dzięki mojemu doświadczeniu z Mathematica. F # zyskuje teraz moją uwagę, a kiedy jest w powszechnym wydaniu, zamierzam go często używać.Moduł F # Seq zaimplementowany w C# dla IEnumerable?

W tej chwili, ponieważ F # nie jest jeszcze w powszechnym wydaniu, naprawdę nie mogę go użyć w kodzie produkcyjnym. LINQ implementuje niektóre z tych operatorów, używając nazw podobnych do SQL (np. "Select" to "map", a "where" to "filter"), ale nie mogę znaleźć żadnej implementacji "fold", "iter" lub "partition".

Czy ktoś widział jakąkolwiek implementację standardowej sekwencji operatorów C#? Czy to jest coś, co ktoś powinien napisać?

Odpowiedz

6
  • krotnie = Aggregate

Tell stosowanie co iter i partition zrobić i możemy wypełnić puste pola. Zgaduję, że iter = SelectMany i partycja może obejmować Skip/Take?


(aktualizacja) Spojrzałem Partition - Oto surowy wdrożenie że robi niektóre z nich:

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; 
     } 
    } 
} 
1

iter istnieje jako metoda w klasie List, który jest ForEach

inaczej:

public static void iter<T>(this IEnumerable<T> source, Action<T> act) 
     { 
      foreach (var item in source) 
      { 
       act(item);     
      } 
     } 
0

Rolowanie własne w języku C# jest interesującym ćwiczeniem, tutaj są ew mojego. (Patrz również here)

Należy pamiętać, że ITER/foreach na IEnumerable jest nieco kontrowersyjna - myślę, bo trzeba „zakończyć” (lub cokolwiek to słowo jest) IEnumerable, aby cokolwiek faktycznie stało.

//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); 
    } 
+2

Odpowiednik mapi istnieje jako przeciążenie Select. Ponadto, wydaje się nieco ciężkim, aby przekonwertować twój przeliczalny na listę, zamiast wykonywać normalną pętlę foreach i wywoływać akcję za każdym razem ... –

+0

Nie wiedziałem o Wybierz przeciążenie, dziękuję za to. Przypuszczam, że ToList był pierwszą rzeczą, o której pomyślałem. – Benjol

14

Jeśli przyjrzysz się uważnie, wiele operacji Seq ma odpowiednik LINQ lub można go łatwo uzyskać. Po prostu spoglądam w dół na list ...

  • 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 jest zdefiniowany następująco:

Podział zbieranie do dwóch zbiorów, zawierający elementy, dla których dany predykat zwraca true i false odpowiednio

Która możemy zaimplementować przy użyciu GroupBy i dwuelementowej tablicy jako krotki biedak:

public static IEnumerable<TSource>[] Partition<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) 
{ 
    return source.GroupBy(predicate).OrderByDescending(g => g.Key).ToArray(); 
} 

Element 0 zawiera wartości rzeczywiste; 1 zawiera fałszywe wartości. GroupBy jest zasadniczo partycji na sterydy.

I wreszcie, Seq.iter i Seq.iteri mapa łatwo 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); 
} 
+0

Problem z 'partycją', gdy wszystkie elementy pasują lub wszystkie zawodzą w predykacie: otrzymujesz tablicę singleton. Zobacz [moja odpowiedź] (http://stackoverflow.com/a/8931453/97846), zainspirowany twoimi. –

1

ToLookup będzie prawdopodobnie lepiej odpowiada 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]; 
+0

... poza tym, że nie jest leniwy. –

0

Oto aktualizacja dahlbyk'spartition rozwiązania.

To zwróciło array[] gdzie „elementem 0 posiada prawdziwych wartości; 1 posiada fałszywych wartości” — ale to nie trzyma, gdy wszystkie elementy pasują albo wszystko zawiedzie orzeczenie, w którym to przypadku masz singleton szyk i świat bólu.

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); 
}