10

我正在玩一个非常简单的程序来获取一个双精度数组并返回标准偏差。这部分有效,但我想让代码更可重用。我想这样做,以便该方法可以接受可以被视为数字的任何类型的参数并返回标准偏差,而不是硬编码双精度类型(就像我最初在这个程序中所做的那样)。一个人是如何做到这一点的,它的正确术语是什么?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


    namespace ConsoleApplication5
    {
        class Program
        {
            static void Main(string[] args)
            {
                double[] avg = { 3.4, 55.6, 10.0, 4.5, 2, 2 };
                double x = avg.Average();
                //first round of testing
                Console.WriteLine("The average of the first array is below ");
                Console.WriteLine(x);
                Console.WriteLine("below should be the standard deviation!");
                Console.WriteLine(CalculateStandardDeviation(avg));
                Console.ReadLine();
                int[] intAvg = { 4, 3, 5, 6, 2 };
                double secondAvg = intAvg.Average();
                Console.WriteLine("The average of the second array is below ");
                Console.WriteLine(secondAvg);
                //this is where the error is happening
                //CalculateStandardDeviation(secondAvg);

            }
            //this is where I tried to make the query more reusable
            public static double CalculateStandardDeviation(IEnumerable<double> values)
            {

                double avg = values.Average();
                double sum = 0;
                foreach (double d in values)
                {
                    sum += Math.Pow((d - avg), 2);

                }

                return Math.Pow(sum / (values.Count() - 1),.5);

            }
        }
    }
4

6 回答 6

7

你可以使用这样的东西:

public static decimal CalculateStandardDeviation<T>(IEnumerable<T> values)
{
    IEnumerable<decimal> decimalValues = values.Select(v => Convert.ToDecimal(v));

    decimal result = 0;

    // calculate standard deviation on decimalValues

    return result;
}

如果包含无法转换为小数的值,它将抛出异常values,但如果这些值是适当的类型,它将起作用,我认为这很有意义。

于 2013-04-16T19:38:05.847 回答
5

不幸的是,所有数字都没有基类。您可以使用通用的运行时检查方法或编译时安全的重载集来做到这一点。

通用方法:

public static T CalculateStandardDeviation(IEnumerable<T> values)
{
    var valueArray = values.Select(Convert.ToDecimal).ToArray();

    //...

    return (T)standardDeviation;
}

使用单个泛型方法的问题在于,您不能对类型参数施加类型约束,从而将其限制为仅限数字类型。您将不得不求助于在运行时失败。没有什么可以阻止您使用字符串、对象、颜色或 HttpWebRequests 等数组调用该方法,除非您确实知道如何计算颜色的标准偏差,否则您可能应该坚持特定数字类型的单独覆盖:

我建议使用该decimal类型作为您的主要实现,然后将所有内容都转换为它。

特定类型的重载:

public static decimal CalculateStandardDeviation(IEnumerable<decimal> values)
{
    //...
}

public static double CalculateStandardDeviation(IEnumerable<double> values)
{
    return (double)CalculateStandardDeviation(values.Select(Convert.ToDecimal));
}

public static int CalculateStandardDeviation(IEnumerable<int> values)
{
    return (int)CalculateStandardDeviation(values.Select(Convert.ToDecimal));
}

// etc...
于 2013-04-16T19:27:57.317 回答
2

使用 C# 泛型。

您的函数签名将是:

public static T CalculateStandardDeviation(IEnumerable<T> values)

你可以像这样使用它:

int stdDev = CalculateStandardDeviation([int-array]);
double stdDev = CalculateStandardDeviation([double-array]);

请点击此链接:

http://msdn.microsoft.com/en-us/library/ms379564%28VS.80%29.aspx

编辑:

要解决泛型类型的平均问题,请查看此库:

如何实现通用方法对不同的值类型进行数学计算

Obs:来自布赖恩的建议。

于 2013-04-16T19:23:32.790 回答
0

编辑 您应该使用 JLRishe 的答案,它比这更优雅。

您可能应该首先将泛型添加到您的方法中,然后使用类型转换器将您的未知输入转换为双精度,如下所示:

public static double CalculateStandardDeviation<TSource>(IEnumerable<TSource> inputs)
{
    var converter = TypeDescriptor.GetConverter(typeof (double));
    if (!converter.CanConvertFrom(typeof(TSource)))
        return 0;

    var values = new List<double>();
    foreach (var value in inputs)
    {
        values.Add((double) converter.ConvertFrom(value));
    }

    // Your logic here ...
    return ...;
}

我没有测试这个片段,但你明白了。

于 2013-04-16T19:27:17.517 回答
0

C# 6.0 将支持将数值的 IEnumerable 作为参数传递给方法

于 2014-12-05T19:24:10.870 回答
0

前言:这个答案建立在 如何验证类型是否重载/支持某个运算符?http://www.codeproject.com/Articles/87438/TinyLisp-A-Language-and-Parser-to-See-LINQ-Express 第二个链接显示了如何编译和评估 linq 表达式。

简而言之,您可以放弃静态类型安全并检查类型在运行时支持特定操作的能力(第一个链接),如果不支持,您可以抛出异常,如下面的示例所示:

void Main()
{
    DoAdd<float>(5,6);
    DoAdd<int>(5,6);
    DoAdd<bool>(true,false);
}


// Define other methods and classes here
static void DoAdd<T>(T in1, T in2){
if(!HasAdd<T>()){throw new Exception("Unsupported Type!");}
 var c1 = Expression.Constant(in1, typeof(T));
 var c2 = Expression.Constant(in2, typeof(T));
 var expression=Expression.Add(c1, c2);
 Expression<Func<T>> lExpression = Expression.Lambda<Func<T>>(expression);
 Func<T> fExpression = lExpression.Compile();
 Console.WriteLine(fExpression());

}


static bool HasAdd<T>() {
    var c = Expression.Constant(default(T), typeof(T));
    try {
        Expression.Add(c, c); // Throws an exception if + is not defined
        return true;
    } catch {
        return false;
    }
}
于 2013-04-16T20:00:42.963 回答