前回の 『「filterM で powerset」をC#に翻訳』はHaskellからの翻訳なのでC#的にいろいろ無理があった。特に再帰のところ。
熟練した C# 使いは再帰を書かない? その通り。ではどうするか。Aggregateを使う。
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
var array = new[] { 1, 2, 3, 4, 5 };
Func<int, IEnumerable<bool>> pred = x => new[] { true, false };
foreach (var l in array.WhereMany(pred))
{
Console.WriteLine(l.ToStringEx());
}
}
}
public static class EnumerableEx
{
public static IEnumerable<IEnumerable<T>> WhereMany<T>(
this IEnumerable<T> source,
Func<T, IEnumerable<bool>> predicates)
{
return source.Aggregate(
Enumerable.Repeat(new LinkedList<T>(), 1),
(acc, x) => acc.SelectMany(q => Accumulate(q, x, predicates))
);
}
private static IEnumerable<LinkedList<T>> Accumulate<T>(
LinkedList<T> q,
T x,
Func<T, IEnumerable<bool>> predicates)
{
foreach(var b in predicates(x))
{
if (b)
{
q.AddLast(x);
yield return q;
q.RemoveLast();
}
else
{
yield return q;
}
}
}
public static string ToStringEx<T>(this IEnumerable<T> source)
{
var strings = source.Select(x => x.ToString());
return "[" + string.Join(", ", strings) + "]";
}
}