我正在写一个通用的roulette selection algorithm。通常,property
是一种原始数字类型,可用于求和每个元素的“得分”。
但是,由于实现是通用的,并且没有办法将属性的类型直接约束为数字基元,我没有明确的方法对值进行求和并按比例选择property
的值。
在下面的代码中,您将注意到我正在尝试将属性的值添加到sum
和rouletteSum
。此代码产生错误,因为PropertyInfo.GetValue()
返回一个我无法转换为原始数字类型的对象,而基本上不会破坏实现的通用性。
我可以采取什么方法来确保算法的一般性,同时仍然能够按比例比较和选择提供的property
的值?
一个考虑因素是将P
约束为IConvertible
,但我想在property
参数中提供基元时会导致一些丑陋的类型转换。
public class RouletteSelectionFunction : ISelectionFunction
{
public string Name => "Roulette";
public T Select<T, P>( IEnumerable<T> elements, Expression<Func<T, P>> property )
where T : class
{
var prop = ( PropertyInfo ) ( ( MemberExpression ) property.Body ).Member;
// Sum all fitnesses and normalize negatives
// by shifting range to minimum of 0
double sum = 0.0;
double lowest = 0.0;
for ( var i = 0; i < elements.Count(); i++ )
{
var value = prop.GetValue( elements.ElementAt( i ) );
sum += value;
if ( value < lowest )
lowest = value;
}
lowest = Math.Abs( lowest );
sum += lowest * elements.Count();
// Roll roulette and select victor
double rouletteSum = 0;
double random = RandomGen.NextDouble() * sum; //RandomGen wraps Random() class and NextDouble() returns number between 0 and 1
for( var i = 0; i < elements.Count(); i++ )
{
rouletteSum += prop.GetValue( elements.ElementAt( i ) );
if ( random <= rouletteSum )
return elements.ElementAt( i );
}
throw new SelectionFailedException( "Roulette Selection could not determine victor" );
}
}
// Call via:
// RouletteSelectionFunction.Select( elements, x => x.Score )
我可以采取什么方法来确保算法的通用性,同时仍然能够按比例比较和选择提供的属性的值?
你没有,至少不容易。 C#从未提供适用于算术抽象的泛型类型系统。
多年来有很多提案。例如,你可以想象在接口中允许静态成员,然后你可以说where T : IAddable<T>
,其中IAddable<T>
是一个接口,承诺在public static T operator +(T, T)
上有一个T
。
您还可以显式传入实现总和的Func<T, T, T>
,依此类推。
但是,您面临的问题基本上是您希望滥用泛型以形成实际上不是通用的特化。我们认为泛型是类似List<T>
的地方,你真的可以列出任何类型的任何类型。你的代码真的是通用的吗?听起来它可以通过简单地说总和为double
来实现。