如何正确处理方法的参数数量决定循环所需数量的情况
本文关键字:循环 情况 决定 数数 正确处理 方法 参数 | 更新日期: 2023-09-27 18:27:45
我需要一些帮助。我有一个我执行的算法,用户可以传递1到5个系数。系数的数量决定了我在算法中需要使用多少循环。我目前有5个私有方法来执行这项工作(我将它们设为私有方法,这样用户就不必担心调用哪一个),还有一个用户可以看到的公共方法。公共方法的唯一目的是根据参数的数量调用适当的私有方法:
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:这是当我使用以下参数运行查询时LINQ查询(inputIndices
)的输出:
分析。系数是一个int[]{2,1,3}
state.Inputs是一个int[]{100200300400500600}
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
查询输出为我提供了输入INDICE的所有唯一组合,我必须在计算中使用这些组合,因为我知道我想使用三个系数。因此,基本上,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
方法中使用的输入。
您可以将问题视为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
的特定参数相对应所需的值。
Calculate()可以被编写为以一种有用的方式处理null组件吗?我在想,如果你只有五个深度循环版本的GetResults(),并且(在单个深度的情况下)a、b、c和d的循环计数只有1,并且为a、b、c和d提交了一个null或类似null的Component,那么你就只有一个处理所有情况的GetResult()方法。当(例如)a==b时跳过的逻辑必须变得稍微复杂一点才能支持这一点。