1

我可以使用一些帮助。我有一个算法,用户可以在其中传递 1 到 5 个系数。系数的数量决定了我需要在算法中使用多少个 for 循环。我目前有 5 个私有方法来执行工作(我将它们设为私有,因此用户不必担心要调用哪个)和 1 个用户可以看到的公共方法。公共方法的唯一目的是根据参数的数量调用适当的私有方法:

public Analysis GetResults(IMDEngineState state, int[] coefficients)
{
    switch (coefficients.Length)
    {
        case 1: return GetResults(state, coefficients[0]);
        case 2: return GetResults(state, coefficients[0], coefficients[1]);
        case 3: return GetResults(state, coefficients[0], coefficients[1], coefficients[2]);
        case 4: return GetResults(state, coefficients[0], coefficients[1], coefficients[2], coefficients[3]);
        case 5: return GetResults(state, coefficients[0], coefficients[1], coefficients[2], coefficients[3], coefficients[4]);
        default:
            throw new ArgumentException("Invalid number of inputs: " + coefficients.Length);
    }
}

我的私有方法如下所示。你会注意到有很多重复的代码。

private Analysis GetResults(IMDEngineState state, int A)
{
    Analysis analysis = new Analysis(new int[] { A });
    state.CurrentEquation = analysis.Equation;

    int combinations = Convert.ToInt32(Math.Pow(2, analysis.Coefficients.Length - 1));
    int numberOfInputs = state.Inputs.Length;
    for (int a = 0; a < numberOfInputs ; a++)
    {
        int resultsFound = 0;
        for (int i = 0; i < combinations; i++)
            resultsFound += Calculate(analysis, state.Outputs, state.Bandwidth,
                                new Component(signs[i][0], A, state.Inputs[a], frequencyFormat));
        if (!ReportProgress(state, combinations, resultsFound))
            return null;
    }            

    return analysis;
}

private Analysis GetResults(IMDEngineState state, int A, int B)
{
    Analysis analysis = new Analysis(new int[] { A, B });
    state.CurrentEquation = analysis.Equation;

    int combinations = Convert.ToInt32(Math.Pow(2, analysis.Coefficients.Length - 1));
    int numberOfInputs = state.Inputs.Length;
    for (int a = 0; a < numberOfInputs ; a++)
    {
        for (int b = 0; b < numberOfInputs ; b++)
        {
            if (a == b)
                continue;

            int resultsFound = 0;
            for (int i = 0; i < combinations; i++)
                resultsFound += Calculate(analysis, state.Outputs, state.Bandwidth,
                                    new Component(signs[i][1], A, state.Inputs[a], frequencyFormat),
                                    new Component(signs[i][0], B, state.Inputs[b], frequencyFormat));
            if (!ReportProgress(state, combinations, resultsFound))
                return null;
        }
    }

    return analysis;
}

private Analysis GetResults(IMDEngineState state, int A, int B, int C)
{
    Analysis analysis = new Analysis(new int[] { A, B, C });
    state.CurrentEquation = analysis.Equation;

    int combinations = Convert.ToInt32(Math.Pow(2, analysis.Coefficients.Length - 1));
    int numberOfInputs = state.Inputs.Length;
    for (int a = 0; a < numberOfInputs ; a++)
    {
        for (int b = 0; b < numberOfInputs ; b++)
        {
            if (a == b)
                continue;

            for (int c = 0; c < numberOfInputs ; c++)
            {
                if (a == c || b == c)
                    continue;

                int resultsFound = 0;
                for (int i = 0; i < combinations; i++)
                    resultsFound += Calculate(analysis, state.Outputs, state.Bandwidth,
                                        new Component(signs[i][2], A, state.Inputs[a], frequencyFormat),
                                        new Component(signs[i][1], B, state.Inputs[b], frequencyFormat),
                                        new Component(signs[i][0], C, state.Inputs[c], frequencyFormat));
                if (!ReportProgress(state, combinations, resultsFound))
                    return null;
            }
        }
    }

    return analysis;
}

private Analysis GetResults(IMDEngineState state, int A, int B, int C, int D)
{
    Analysis analysis = new Analysis(new int[] { A, B, C, D });
    state.CurrentEquation = analysis.Equation;

    int combinations = Convert.ToInt32(Math.Pow(2, analysis.Coefficients.Length - 1));
    int numberOfInputs = state.Inputs.Length;
    for (int a = 0; a < numberOfInputs ; a++)
    {
        for (int b = 0; b < numberOfInputs ; b++)
        {
            if (a == b)
                continue;

            for (int c = 0; c < numberOfInputs ; c++)
            {
                if (a == c || b == c)
                    continue;

                for (int d = 0; d < numberOfInputs ; d++)
                {
                    if (a == d || b == d || c == d)
                        continue;

                    int resultsFound = 0;
                    for (int i = 0; i < combinations; i++)
                        resultsFound += Calculate(analysis, state.Outputs, state.Bandwidth,
                                            new Component(signs[i][3], A, state.Inputs[a], frequencyFormat),
                                            new Component(signs[i][2], B, state.Inputs[b], frequencyFormat),
                                            new Component(signs[i][1], C, state.Inputs[c], frequencyFormat),
                                            new Component(signs[i][0], D, state.Inputs[d], frequencyFormat));
                    if (!ReportProgress(state, combinations, resultsFound))
                        return null;
                }
            }
        }
    }

    return analysis;
}

private Analysis GetResults(IMDEngineState state, int A, int B, int C, int D, int E)
{
    Analysis analysis = new Analysis(new int[] { A, B, C, D, E });
    state.CurrentEquation = analysis.Equation;

    int combinations = Convert.ToInt32(Math.Pow(2, analysis.Coefficients.Length - 1));
    int numberOfInputs = state.Inputs.Length;
    for (int a = 0; a < numberOfInputs ; a++)
    {
        for (int b = 0; b < numberOfInputs ; b++)
        {
            if (a == b)
                continue;

            for (int c = 0; c < numberOfInputs ; c++)
            {
                if (a == c || b == c)
                    continue;

                for (int d = 0; d < numberOfInputs ; d++)
                {
                    if (a == d || b == d || c == d)
                        continue;

                    for (int e = 0; e < numberOfInputs ; e++)
                    {
                        if (a == e || b == e || c == e || d == e)
                            continue;

                        int resultsFound = 0;
                        for (int i = 0; i < combinations; i++)
                            resultsFound += Calculate(analysis, state.Outputs, state.Bandwidth,
                                                new Component(signs[i][4], A, state.Inputs[a], frequencyFormat),
                                                new Component(signs[i][3], B, state.Inputs[b], frequencyFormat),
                                                new Component(signs[i][2], C, state.Inputs[c], frequencyFormat),
                                                new Component(signs[i][1], D, state.Inputs[d], frequencyFormat),
                                                new Component(signs[i][0], E, state.Inputs[e], frequencyFormat));
                        if (!ReportProgress(state, combinations, resultsFound))
                            return null;
                    }
                }                       
            }
        }
    }

    return analysis;
}

我真的更喜欢只有 1 种方法而不是 5 种方法,这样代码的维护就更容易了。我担心将来我必须记住在进行更改时更新所有 5 种方法。这也更容易出错。

我确实尝试使用递归来做到这一点,但我觉得代码的可读性受到了负面影响。更难理解到底发生了什么,这也让我担心将来要更改此代码时。

有没有人有什么建议?我想在不重复的情况下找到可读性的正确平衡。

编辑:回答

感谢Servy的帮助,这就是我最终得到的结果。我现在只有一种公共方法。有关如何完成 LINQ 的信息,请参阅他的回答。

public Analysis GetResults(IMDEngineState state, int[] coefficients)
{
    if (coefficients.Length < 1 || coefficients.Length > 5)
        throw new ArgumentException("Invalid number of inputs: " + coefficients.Length);

    Analysis analysis = new Analysis(coefficients);
    state.CurrentEquation = analysis.Equation;

    var inputIndices = analysis.Coefficients.Select(input => Enumerable.Range(0, state.Inputs.Length))
                                            .CartesianProduct()
                                            .Where(seq => seq.Count() == seq.Distinct().Count());

    foreach (var indices in inputIndices)
    {
        if (!ReportProgress(state, Calculate(state, analysis, indices.ToArray())))
            return null;
    }

    return analysis;
}

编辑:回答 Phpdna 的评论

@Phpdna:inputIndices当我使用以下参数运行查询时,这是 LINQ 查询 () 的输出:

analysis.Coefficients 是一个 int[] { 2, 1, 3 }

state.Inputs 是一个 int[] { 100, 200, 300, 400, 500, 600 }

0,1,2       1,0,2       2,0,1       3,0,1       4,0,1       5,0,1
0,1,3       1,0,3       2,0,3       3,0,2       4,0,2       5,0,2
0,1,4       1,0,4       2,0,4       3,0,4       4,0,3       5,0,3
0,1,5       1,0,5       2,0,5       3,0,5       4,0,5       5,0,4
0,2,1       1,2,0       2,1,0       3,1,0       4,1,0       5,1,0
0,2,3       1,2,3       2,1,3       3,1,2       4,1,2       5,1,2
0,2,4       1,2,4       2,1,4       3,1,4       4,1,3       5,1,3
0,2,5       1,2,5       2,1,5       3,1,5       4,1,5       5,1,4
0,3,1       1,3,0       2,3,0       3,2,0       4,2,0       5,2,0
0,3,2       1,3,2       2,3,1       3,2,1       4,2,1       5,2,1
0,3,4       1,3,4       2,3,4       3,2,4       4,2,3       5,2,3
0,3,5       1,3,5       2,3,5       3,2,5       4,2,5       5,2,4
0,4,1       1,4,0       2,4,0       3,4,0       4,3,0       5,3,0
0,4,2       1,4,2       2,4,1       3,4,1       4,3,1       5,3,1
0,4,3       1,4,3       2,4,3       3,4,2       4,3,2       5,3,2
0,4,5       1,4,5       2,4,5       3,4,5       4,3,5       5,3,4
0,5,1       1,5,0       2,5,0       3,5,0       4,5,0       5,4,0
0,5,2       1,5,2       2,5,1       3,5,1       4,5,1       5,4,1
0,5,3       1,5,3       2,5,3       3,5,2       4,5,2       5,4,2
0,5,4       1,5,4       2,5,4       3,5,4       4,5,3       5,4,3

查询输出为我提供了输入 INDICES 的所有唯一组合,我必须在计算中使用它们,因为我知道我想使用三个系数。所以基本上,长度analysis.Coefficients决定了查询输出的每个数组中的元素数量。analysis.Coefficients和中的实际值state.Inputs无关紧要(对于查询 - 我使用Calculate方法中的值,因此它们确实对我有用)。

因此,作为查询的结果,我现在将Calculate使用以下信息运行我的方法,将查询输出 ( indices) 转换为对我来说有意义的数据......(我只是以第一列为例)

analysis.Coefficients[0]*state.Inputs[0], analysis.Coefficients[1]*state.Inputs[1], analysis.Coefficients[2]*state.Inputs[2]
analysis.Coefficients[0]*state.Inputs[0], analysis.Coefficients[1]*state.Inputs[1], analysis.Coefficients[2]*state.Inputs[3]
analysis.Coefficients[0]*state.Inputs[0], analysis.Coefficients[1]*state.Inputs[1], analysis.Coefficients[2]*state.Inputs[4]
analysis.Coefficients[0]*state.Inputs[0], analysis.Coefficients[1]*state.Inputs[1], analysis.Coefficients[2]*state.Inputs[5]
analysis.Coefficients[0]*state.Inputs[0], analysis.Coefficients[1]*state.Inputs[2], analysis.Coefficients[2]*state.Inputs[1]
analysis.Coefficients[0]*state.Inputs[0], analysis.Coefficients[1]*state.Inputs[2], analysis.Coefficients[2]*state.Inputs[3]
analysis.Coefficients[0]*state.Inputs[0], analysis.Coefficients[1]*state.Inputs[2], analysis.Coefficients[2]*state.Inputs[4]
analysis.Coefficients[0]*state.Inputs[0], analysis.Coefficients[1]*state.Inputs[2], analysis.Coefficients[2]*state.Inputs[5]
analysis.Coefficients[0]*state.Inputs[0], analysis.Coefficients[1]*state.Inputs[3], analysis.Coefficients[2]*state.Inputs[1]
analysis.Coefficients[0]*state.Inputs[0], analysis.Coefficients[1]*state.Inputs[3], analysis.Coefficients[2]*state.Inputs[2]
analysis.Coefficients[0]*state.Inputs[0], analysis.Coefficients[1]*state.Inputs[3], analysis.Coefficients[2]*state.Inputs[4]
analysis.Coefficients[0]*state.Inputs[0], analysis.Coefficients[1]*state.Inputs[3], analysis.Coefficients[2]*state.Inputs[5]
analysis.Coefficients[0]*state.Inputs[0], analysis.Coefficients[1]*state.Inputs[4], analysis.Coefficients[2]*state.Inputs[1]
analysis.Coefficients[0]*state.Inputs[0], analysis.Coefficients[1]*state.Inputs[4], analysis.Coefficients[2]*state.Inputs[2]
analysis.Coefficients[0]*state.Inputs[0], analysis.Coefficients[1]*state.Inputs[4], analysis.Coefficients[2]*state.Inputs[3]
analysis.Coefficients[0]*state.Inputs[0], analysis.Coefficients[1]*state.Inputs[4], analysis.Coefficients[2]*state.Inputs[5]
analysis.Coefficients[0]*state.Inputs[0], analysis.Coefficients[1]*state.Inputs[5], analysis.Coefficients[2]*state.Inputs[1]
analysis.Coefficients[0]*state.Inputs[0], analysis.Coefficients[1]*state.Inputs[5], analysis.Coefficients[2]*state.Inputs[2]
analysis.Coefficients[0]*state.Inputs[0], analysis.Coefficients[1]*state.Inputs[5], analysis.Coefficients[2]*state.Inputs[3]
analysis.Coefficients[0]*state.Inputs[0], analysis.Coefficients[1]*state.Inputs[5], analysis.Coefficients[2]*state.Inputs[4]

同样,使用相同的第一列,这减少到...

2*100, 1*200, 3*300 =   200, 200, 900
2*100, 1*200, 3*400 =   200, 200, 1200
2*100, 1*200, 3*500 =   200, 200, 1500
2*100, 1*200, 3*600 =   200, 200, 1800
2*100, 1*300, 3*200 =   200, 300, 600
2*100, 1*300, 3*400 =   200, 300, 1200
2*100, 1*300, 3*500 =   200, 300, 1500
2*100, 1*300, 3*600 =   200, 300, 1800
2*100, 1*400, 3*200 =   200, 400, 600
2*100, 1*400, 3*300 =   200, 400, 900
2*100, 1*400, 3*500 =   200, 400, 1500
2*100, 1*400, 3*600 =   200, 400, 1800
2*100, 1*500, 3*200 =   200, 500, 600
2*100, 1*500, 3*300 =   200, 500, 900
2*100, 1*500, 3*400 =   200, 500, 1200
2*100, 1*500, 3*600 =   200, 500, 1800
2*100, 1*600, 3*200 =   200, 600, 600
2*100, 1*600, 3*300 =   200, 600, 900
2*100, 1*600, 3*400 =   200, 600, 1200
2*100, 1*600, 3*500 =   200, 600, 1500

所以我终于有了将在我的Calculate方法中使用的输入。

4

2 回答 2

2

您可以将您的问题视为 N 个序列的笛卡尔积,其中每个序列是从零到您的 N 个指定值之一的数字。

Eric Lippert 写了一篇精彩的文章,解释了如何使用 LINQ 生成 N 个序列的笛卡尔积。他最后得出的代码是:

static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences)
{
    IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };
    return sequences.Aggregate(
      emptyProduct,
      (accumulator, sequence) =>
        from accseq in accumulator
        from item in sequence
        select accseq.Concat(new[] { item }));
}

您还可以使用下面的辅助方法来应用仅使用所有值都是唯一的序列的约束:

public static bool AreUnique<T>(this IEnumerable<T> sequence)
{
    var set = new HashSet<T>();
    foreach (var item in sequence)
        if (!set.Add(item))
            return false;
    return true;
}

这是一个查询,它将为您提供一系列 int 序列,其中每个子序列是内部循环的特定迭代的所有系数。

var query = coefficients.Select(coeff => Enumerable.Range(0, coeff))
        .CartesianProduct()
        .Where(sequence => sequence.AreUnique());

请注意,要继续进行此重构,您需要进行编辑Calculate,以便它可以采用值序列(或集合),而不是具有 1-5 个参数。然后,您可以将每个子序列的每个值映射到与特定参数对应所需的值Calculate

于 2013-08-14T18:35:07.997 回答
0

是否可以编写 Calculate() 以便它以有用的方式处理 null-ish 组件?我在想你是否只有 GetResults() 的五深循环版本,让循环计数(在单深度情况下)a、b、c 和 d 仅为 1,并提交 null 或A、B、C 和 D 的类 null 组件,那么您将只有一个处理所有情况的 GetResults() 方法。当(例如)a == b 时跳过的逻辑必须变得更复杂一些才能支持这一点。

于 2013-08-14T18:14:22.290 回答