Using Lambdas and Expressions
Here is the example code you asked for
class Program
{
static void Main(string[] args)
{
var avg_float = new Progressing_Average<float>();
avg_float.AddValue(1f);
avg_float.AddValue(2f);
avg_float.AddValue(3f);
Console.WriteLine($"ave={avg_float.Value:f4}");
var avg_dec = new Progressing_Average<decimal>();
avg_dec.AddValue(1m);
avg_dec.AddValue(2m);
avg_dec.AddValue(3m);
Console.WriteLine($"ave={avg_dec.Value:c}");
}
}
with sample output
ave=2.0000
ave=$2.00
Generic Math
Ever since NET 3.5, you can do generic math using Expressions and Lambda functions. There is a bit of setup that is needed, but the example below is fairly straightforward.
Some notes:
Define generic parameter T and restrict it to numeric types. Newer NET implementations have better ways of doing this but for compatibility, with .NET Framework I use the following where constraint in T
Define static methods (actually static anonymous functions) for the required math operations. Some basic ones are +, -, *, / and the conversion from int to T for mixing _Count.
These methods are built using a static constructor for the type and are called once for each different T used.
Replace the math operators with the equivalent functions. Like a+b becomes Add(a,b).
Class Definition
public class Progressing_Average<T> where T: struct,
IComparable,
IComparable<T>,
IConvertible,
IEquatable<T>,
IFormattable
{
static Progressing_Average()
{
var a = Expression.Parameter(typeof(T));
var b = Expression.Parameter(typeof(T));
var n = Expression.Parameter(typeof(int));
Add = Expression.Lambda<Func<T, T, T>>(Expression.Add(a, b), a, b).Compile();
Sub = Expression.Lambda<Func<T, T, T>>(Expression.Subtract(a, b), a, b).Compile();
Mul = Expression.Lambda<Func<T, T, T>>(Expression.Multiply(a, b), a, b).Compile();
Div = Expression.Lambda<Func<T, T, T>>(Expression.Divide(a, b), a, b).Compile();
FromInt = Expression.Lambda<Func<int, T>>(Expression.Convert(n, typeof(T)), n).Compile();
}
static Func<T, T, T> Add { get; }
static Func<T, T, T> Sub { get; }
static Func<T, T, T> Mul { get; }
static Func<T, T, T> Div { get; }
static Func<int, T> FromInt { get; }
public Progressing_Average()
{
_Count = 0;
Value = FromInt(0);
}
public T Value { get; private set; }
private int _Count { get; set; }
public void AddValue(T input)
{
if (_Count == int.MaxValue)
{
throw new IndexOutOfRangeException("max amount has been reached! use preciseaverage or moving avg instead!");
}
_Count++;
Value = Add(Value, Div(Sub(input, Value), FromInt(_Count)));
}
}
Support for User Defined Types
If you relax the constraints a bit, you can even use your own types above.
Define the processing class as
public class Progressing_Average<T> where T: struct,
IEquatable<T>,
IFormattable
and a user-defined type, such as
public readonly struct Point :
IEquatable<Point>,
IFormattable
{
public Point(float x, float y) : this()
{
X = x;
Y = y;
}
public float X { get; }
public float Y { get; }
public static Point operator +(Point a, Point b)
=> new Point(a.X + b.X, a.Y + b.Y);
public static Point operator -(Point a, Point b)
=> new Point(a.X - b.X, a.Y - b.Y);
public static Point operator *(Point a, Point b)
=> new Point(a.X * b.X, a.Y * b.Y);
public static Point operator /(Point a, Point b)
=> new Point(a.X / b.X, a.Y / b.Y);
public static implicit operator Point(int x)
=> new Point(x,x);
public static implicit operator Point(float x)
=> new Point(x,x);
public static implicit operator Point(decimal x)
=> new Point((float)x,(float)x);
public static implicit operator Point(double x)
=> new Point((float)x,(float)x);
#region IFormattable Members
public string ToString(string formatting, IFormatProvider provider)
{
return $"({X},{Y})";
}
public string ToString(string formatting)
=> ToString(formatting, null);
public override string ToString()
=> ToString("g");
#endregion
#region IEquatable Members
/// <summary>
/// Equality overrides from <see cref="System.Object"/>
/// </summary>
/// <param name="obj">The object to compare this with</param>
/// <returns>False if object is a different type, otherwise it calls <code>Equals(Point)</code></returns>
public override bool Equals(object obj)
{
return obj is Point item && Equals(item);
}
/// <summary>
/// Checks for equality among <see cref="Point"/> classes
/// </summary>
/// <returns>True if equal</returns>
public bool Equals(Point other)
{
return X.Equals(other.X)
&& Y.Equals(other.Y);
}
/// <summary>
/// Calculates the hash code for the <see cref="Point"/>
/// </summary>
/// <returns>The int hash value</returns>
public override int GetHashCode()
{
unchecked
{
int hc = -1817952719;
hc = (-1521134295) * hc + X.GetHashCode();
hc = (-1521134295) * hc + Y.GetHashCode();
return hc;
}
}
public static bool operator ==(Point target, Point other) { return target.Equals(other); }
public static bool operator !=(Point target, Point other) { return !target.Equals(other); }
#endregion
}
Now watch as you can use the above struct to get averages
class Program
{
static void Main(string[] args)
{
var avg_vec = new Progressing_Average<Point>();
avg_vec.AddValue(new Point(1, 2));
avg_vec.AddValue(new Point(2, 3));
avg_vec.AddValue(new Point(3, 4));
Console.WriteLine($"ave={avg_vec.Value}");
}
}
with output
ave=(2,3)
There is some skullduggery going on here, because behind the scenes the expression Expression.Convert() automatically calls any implicit operator for conversions, as well as any user defined arithmetic operators such as operator + (a,b).
where T : INumber<T>apply(int input)vsapply(double input)