Je pense que vous trouverez probablement ce problème beaucoup plus facile à résoudre dans F # si vous repassez ce qui précède d'une manière déclarative plutôt que dans un impératif manière. Voici mon approche de la réécriture déclarative de ce qui précède:
Nous avons d'abord besoin d'une classe wrapper pour décorer nos objets avec une propriété portant le grade.
class Ranked<T> {
public T Value { get; private set; }
public double Rank { get; private set; }
public Ranked(T value, double rank) {
this.Value = value;
this.Rank = rank;
}
}
Voici donc votre algorithme de manière déclarative. Notez que elements
est votre séquence d'entrée et la séquence résultante est dans le même ordre que elements
. Le délégué func
est la valeur que vous souhaitez classer elements
par.
static class IEnumerableExtensions {
public static IEnumerable<Ranked<T>> Rank<T, TRank>(
this IEnumerable<T> elements,
Func<T, TRank> func
) {
var groups = elements.GroupBy(x => func(x));
var ranks = groups.OrderBy(g => g.Key)
.Aggregate(
(IEnumerable<double>)new List<double>(),
(x, g) =>
x.Concat(
Enumerable.Repeat(
Enumerable.Range(x.Count() + 1, g.Count()).Sum()/(double)g.Count(),
g.Count()
)
)
)
.GroupBy(r => r)
.Select(r => r.Key)
.ToArray();
var dict = groups.Select((g, i) => new { g.Key, Index = i })
.ToDictionary(x => x.Key, x => ranks[x.Index]);
foreach (T element in elements) {
yield return new Ranked<T>(element, dict[func(element)]);
}
}
}
Utilisation:
class MyClass {
public double Score { get; private set; }
public MyClass(double score) { this.Score = score; }
}
List<MyClass> list = new List<MyClass>() {
new MyClass(1.414),
new MyClass(2.718),
new MyClass(2.718),
new MyClass(2.718),
new MyClass(1.414),
new MyClass(3.141),
new MyClass(3.141),
new MyClass(3.141),
new MyClass(1.618)
};
foreach(var item in list.Rank(x => x.Score)) {
Console.WriteLine("Score = {0}, Rank = {1}", item.Value.Score, item.Rank);
}
Sortie:
Score = 1.414, Rank = 1.5
Score = 2.718, Rank = 3
Score = 2.718, Rank = 3
Score = 2.718, Rank = 3
Score = 1.414, Rank = 1.5
Score = 3.141, Rank = 5
Score = 3.141, Rank = 5
Score = 3.141, Rank = 5
Score = 1.618, Rank = 8
Notez que je ne demande pas la séquence d'entrée à commander. Le code résultant est plus simple si vous appliquez une telle exigence sur la séquence d'entrée. Notez en outre que nous ne monnayons pas la séquence d'entrée et que nous ne modifions pas non plus les éléments d'entrée. Cela rend F # heureux. De là, vous devriez être capable de réécrire facilement ceci en F #.
@kvb J'espère que ça ne te dérange pas, je reformaté votre code pour jouer agréable avec la syntaxe surligneur – gradbot
On dirait que vous éditez encore, je arrête. – gradbot
@gradbot - Pas de problème, merci de le nettoyer. – kvb