C#通用算法将参数约束为数字
C# Generic Algorithm constraining parameter to number
我正在写一个通用的roulette selection algorithm。通常,property
是原始数字类型,可用于每个元素的 "score" 的求和。
但是,由于实现是通用的,并且无法将 属性 的类型直接限制为数字基元,因此我没有明确的方法来对值求和 select 与 property
的值成比例。
在下面的代码中,您会注意到我试图将 属性 的值添加到 sum
和 rouletteSum
。此代码会产生错误,因为 PropertyInfo.GetValue()
returns 一个对象,我无法在不破坏实现的通用性的情况下将其转换为原始数字类型。
我可以采取什么方法来确保算法的通用性,同时仍然能够按比例比较 select 所提供的 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 )
What approach can I take to assure genericness of the algorithm while still being able to proportionally compare and select from the values of the provided property?
你不会,至少不容易。 C# 从未提供适用于算术抽象的泛型类型系统。
多年来有很多提案。例如,您可以想象在接口中允许静态成员,然后您可以说 where T : IAddable<T>
,其中 IAddable<T>
是一个承诺在 T
上有一个 public static T operator +(T, T)
的接口。
您还可以显式传递一个 Func<T, T, T>
来实现您的总和,等等。
但是您面临的问题本质上是您希望滥用泛型以形成实际上不是 泛型 的专业化。我们认为泛型类似于 List<T>
,您可以在其中真正、真正地列出 任何类型 。您的代码实际上是通用的吗?听起来它可以通过简单地说总和为 double
来工作。
我正在写一个通用的roulette selection algorithm。通常,property
是原始数字类型,可用于每个元素的 "score" 的求和。
但是,由于实现是通用的,并且无法将 属性 的类型直接限制为数字基元,因此我没有明确的方法来对值求和 select 与 property
的值成比例。
在下面的代码中,您会注意到我试图将 属性 的值添加到 sum
和 rouletteSum
。此代码会产生错误,因为 PropertyInfo.GetValue()
returns 一个对象,我无法在不破坏实现的通用性的情况下将其转换为原始数字类型。
我可以采取什么方法来确保算法的通用性,同时仍然能够按比例比较 select 所提供的 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 )
What approach can I take to assure genericness of the algorithm while still being able to proportionally compare and select from the values of the provided property?
你不会,至少不容易。 C# 从未提供适用于算术抽象的泛型类型系统。
多年来有很多提案。例如,您可以想象在接口中允许静态成员,然后您可以说 where T : IAddable<T>
,其中 IAddable<T>
是一个承诺在 T
上有一个 public static T operator +(T, T)
的接口。
您还可以显式传递一个 Func<T, T, T>
来实现您的总和,等等。
但是您面临的问题本质上是您希望滥用泛型以形成实际上不是 泛型 的专业化。我们认为泛型类似于 List<T>
,您可以在其中真正、真正地列出 任何类型 。您的代码实际上是通用的吗?听起来它可以通过简单地说总和为 double
来工作。