我需要帮助来理解函数是如何工作的;它是一个带有yield return的recursive
函数,但我无法弄清楚它是如何工作的。它用于计算一组数据的cumulative
密度函数(近似值)
非常感谢大家。
/// Approximates the cumulative density through a recursive procedure
/// estimating counts of regions at different resolutions.
/// </summary>
/// <param name="data">Source collection of integer values</param>
/// <param name="maximum">The largest integer in the resulting cdf (it has to be a power of 2...</param>
/// <returns>A list of counts, where entry i is the number of records less than i</returns>
public static IEnumerable<int> FUNCT(IEnumerable<int> data, int max)
{
if (max == 1)
{
yield return data.Count();
}
else
{
var t = data.Where(x => x < max / 2);
var f = data.Where(x => x > max / 2);
foreach (var value in FUNCT(t, max / 2))
yield return value;
var count = t.Count();
f = f.Select(x => x - max / 2);
foreach (var value in FUNCT(f, max / 2))
yield return value + count;
}
}
答案 0 :(得分:1)
本质上,使用yield return函数的IEnumerable函数与传统的递归函数略有不同。作为基本情况,假设你有:
IEnumerable<int> F(int n)
{
if (n == 1)
{
yield return 1;
yield return 2;
// implied yield return break;
}
// Enter loop 1
foreach (var v in F(n - 1))
yield return v;
// End loop 1
int sum = 5;
// Enter loop 2
foreach (var v in F(n - 1))
yield return v + sum;
// End loop 2
// implied yield return break;
}
void Main()
{
foreach (var v in F(2))
Console.Write(v);
// implied return
}
F
获取原始FUNCT
的基本内容。如果我们称之为F(2),那么走过收益率:
F(2)
| F(1)
| | yield return 1
| yield return 1
Console.Write(1);
| | yield return 2
| yield return 2
Console.Write(2)
| | RETURNS
| sum = 5;
| F(1)
| | yield return 1
| yield return 1 + 5
Console.Write(6)
| | yield return 2
| yield return 2 + 5
Console.Write(7)
| | RETURNS
| RETURNS
RETURNS
并打印1267
。请注意,yield return
语句会对调用者产生控制权,但下一次迭代会导致函数继续前一次产生的位置。
CDF方法确实增加了一些额外的复杂性,但并不多。递归将集合分成两部分,并计算每个部分的CDF,直到max = 1。然后该函数计算元素的数量并产生它,每个产量递归地传递到封闭循环。
要完成FUNCT
,请假设您使用data=[0,1,0,1,2,3,2,1]
和max=4
。然后运行该方法,使用上面作为驱动程序的相同Main
函数,产生:
FUNCT([0,1,0,1,2,3,2,1], 4)
| max/2 = 2
| t = [0,1,0,1,1]
| f = [3] // (note: per my comment to the original question,
| // should be [2,3,2] to get true CDF. The 2s are
| // ignored since the method uses > max/2 rather than
| // >= max/2.)
| FUNCT(t,max/2) = FUNCT([0,1,0,1,1], 2)
| | max/2 = 1
| | t = [0,0]
| | f = [] // or [1,1,1]
| | FUNCT(t, max/2) = FUNCT([0,0], 1)
| | | max = 1
| | | yield return data.count = [0,0].count = 2
| | yield return 2
| yield return 2
Console.Write(2)
| | | RETURNS
| | count = t.count = 2
| | F(f, max/2) = FUNCT([], 1)
| | | max = 1
| | | yield return data.count = [].count = 0
| | yield return 0 + count = 2
| yield return 2
Console.Write(2)
| | | RETURNS
| | RETURNS
| count = t.Count() = 5
| f = f - max/2 = f - 2 = [1]
| FUNCT(f, max/2) = FUNCT([1], 2)
| | max = 2
| | max/2 = 1
| | t = []
| | f = [] // or [1]
| | FUNCT(t, max/2) = funct([], 1)
| | | max = 1
| | | yield return data.count = [].count = 0
| | yield return 0
| yield return 0 + count = 5
Console.Write(5)
| | | RETURNS
| | count = t.count = [].count = 0
| | f = f - max/2 = []
| | F(f, max/2) = funct([], 1)
| | | max = 1
| | | yield return data.count = [].count = 0
| | yield return 0 + count = 0 + 0 = 0
| yield return 0 + count = 0 + 5 = 5
Console.Write(5)
| | RETURNS
| RETURNS
RETURNS
因此返回值(2,2,5,5)。 (使用>=
将产生值(2,5,7,8) - 请注意,这些是非负积分数据的缩放CDF的精确值,而不是近似值。
答案 1 :(得分:0)
有趣的问题。假设您了解yield的工作原理,对函数的评论(在您的问题中)非常有用。我已经对代码进行了评论,因为我理解它可能有所帮助:
public static IEnumerable<int> FUNCT(IEnumerable<int> data, int max)
{
if (max == 1)
{
// Effectively the end of the recursion.
yield return data.Count();
}
else
{
// Split the data into two sets
var t = data.Where(x => x < max / 2);
var f = data.Where(x => x > max / 2);
// In the set of smaller numbers, recurse to split it again
foreach (var value in FUNCT(t, max / 2))
yield return value;
// For the set of smaller numbers, get the count.
var count = t.Count();
// Shift the larger numbers so they are in the smaller half.
// This allows the recursive function to reach an end.
f = f.Select(x => x - max / 2);
// Recurse but add the count of smaller numbers. We already know there
// are at least 'count' values which are less than max / 2.
// Recurse to find out how many more there are.
foreach (var value in FUNCT(f, max / 2))
yield return value + count;
}
}