There is a full featured support of countable sets in .NET: IEnumerable<T>
. What about uncountable sets; sets defined by predicate? How can they be manipulated and interact with IEnumerable<T>
?
UPDATED: Countable and uncountable sets
Was: Condition-class.
Demo (you can play with it here online):
using static System.Console;
using static System.String;
class Program
{
static void Main(string[] args)
{
var NullOrEmpty = new Set<string>(string.IsNullOrEmpty);
var NullOrWhiteSpace = new Set<string>(string.IsNullOrWhiteSpace);
var WhiteSpace = NullOrWhiteSpace - NullOrEmpty;
WriteLine(WhiteSpace * " "); // True
var LowIncome = new Set<int>(i => i < 30000);
var HighIncome = new Set<int>(i => i > 140000);
var MiddleIncome = !LowIncome && !HighIncome;
var salaries = new[] { 25000, 40000, 35000, 80000, 65000, 120000, 200000 };
WriteLine(Join(",", salaries - MiddleIncome)); // 25000, 200000
}
}
Where full set of operations is defined as:
class Set<T>
{
public Set(Predicate<T> predicate)
{
Predicate = predicate;
}
public static bool operator *(Set<T> left, T right) =>
left.Predicate(right);
public static bool operator *(T left, Set<T> right) =>
right.Predicate(left);
public static Set<T> operator *(Set<T> left, Set<T> right) =>
new Set<T>(i => left.Predicate(i) && right.Predicate(i));
public static IEnumerable<T> operator *(Set<T> left, IEnumerable<T> right) =>
right.Where(i => left.Predicate(i));
public static IEnumerable<T> operator *(IEnumerable<T> left, Set<T> right) =>
left.Where(i => right.Predicate(i));
public static Set<T> operator +(Set<T> left, T right) =>
new Set<T>(i => left.Predicate(i) || right.Equals(i));
public static Set<T> operator +(T left, Set<T> right) =>
new Set<T>(i => left.Equals(i) || right.Predicate(i));
public static Set<T> operator +(Set<T> left, Set<T> right) =>
new Set<T>(i => left.Predicate(i) || right.Predicate(i));
public static Set<T> operator +(Set<T> left, IEnumerable<T> right) =>
new Set<T>(i => left.Predicate(i) || right.Contains(i));
public static Set<T> operator +(IEnumerable<T> left, Set<T> right) =>
new Set<T>(i => left.Contains(i) || right.Predicate(i));
public static Set<T> operator -(Set<T> left, T right) =>
new Set<T>(i => left.Predicate(i) && !right.Equals(i));
public static Set<T> operator -(T left, Set<T> right) =>
new Set<T>(i => left.Equals(i) && !right.Predicate(i));
public static Set<T> operator -(Set<T> left, Set<T> right) =>
new Set<T>(i => left.Predicate(i) && !right.Predicate(i));
public static Set<T> operator -(Set<T> left, IEnumerable<T> right) =>
new Set<T>(i => left.Predicate(i) && !right.Contains(i));
public static IEnumerable<T> operator -(IEnumerable<T> left, Set<T> right) =>
left.Where(i => !right.Predicate(i));
public static bool operator true(Set<T> x) => false;
public static bool operator false(Set<T> x) => false;
public static Set<T> operator |(Set<T> left, Set<T> right) =>
new Set<T>(v => left.Predicate(v) || right.Predicate(v));
public static Set<T> operator &(Set<T> left, Set<T> right) =>
new Set<T>(v => left.Predicate(v) && right.Predicate(v));
public static Set<T> operator !(Set<T> set) =>
new Set<T>(i => !set.Predicate(i));
Predicate<T> Predicate { get; }
}
Does this set of operations look mathematically correct?
var WhiteSpace = NullOrWhiteSpace - NullOrEmpty;
:-D null - null = " " LOL magic \$\endgroup\$ – t3chb0t Jun 14 '16 at 14:02if(set)
? And why should that always be false? Also I don't see why you implement union and intersection using both the logical and arithmetical operations; can you say why you made this design decision? What does it buy the user, aside from confusion and errors in operator precedence? \$\endgroup\$ – Eric Lippert Jun 14 '16 at 16:48