我最近遇到了Pengyang在这里写的以下代码片段: What is the best way to find all combinations of items in an array?
static IEnumerable<IEnumerable<T>>
GetKCombs<T>(IEnumerable<T> list, int length) where T : IComparable
{
if (length == 1) return list.Select(t => new T[] { t });
return GetKCombs(list, length - 1)
.SelectMany(t => list.Where(o => o.CompareTo(t.Last()) > 0),
(t1, t2) => t1.Concat(new T[] { t2 }));
}
使用列表{1,2,3,4}调用GetKCombs,长度为2将返回:
{1,2} {1,3} {1,4} {2,3} {2,4} {3,4}
代码工作正常,但我不明白这段代码是如何以及为什么有效的,如果有人能向我解释,我将不胜感激。
答案 0 :(得分:1)
一般来说,递归方法的作用是首先将输入列表减少为单个元素数组的数组(退出条件),并递归地将1个元素添加到每个单个元素数组的数组中,以使结果成为一个2元素数组的数组,并连续重复添加1个元素,直到元素数组中的元素数量变为您需要的长度,并且添加的一个条件是数组中的最后一个元素小于从输入列表添加到数组的元素。 :)
(我知道它比以前更令人困惑..递归LINQ会这样做)
所以让我们举个例子。
list = { 1, 2, 3, 4 } // IEnumerable<int>, T => int
length = 2
如果你注意到GetKCombs方法,它会递归,直到你达到长度== 1的条件..
所以当你传入length = 2时,你实际上是这样做的:
return list.Select(t => new T[] { t }) // we substituted the call for length = 1
.SelectMany(t => list.Where(o => o.CompareTo(t.Last()) > 0),
(t1, t2) => t1.Concat(new T[] { t2 }));
list.Select(t => new T[] { t })
基本上返回一个元素数组,其中元素本身是1元素数组。
即输入列表中的每个元素。
{ {1}, {2}, {3}, {4} }
我们可以将此结果称为“基础”。将其作为基础,再次对输入列表中的每个元素,这大于基础结果{1, 2, 3, 4}
的每个元素,我们创建对。
即。在输入中为1,我们将匹配2,3,4与基数。对于2,我们将匹配3和4。 对于3,我们将匹配4 这是什么
list.Where(o => o.CompareTo(t.Last()) > 0
确实
一旦我们找到这对{1}
和{2, 3, 4}
,我们就会通过在选择查询中连接它们来创建2个项目元组。(t1, t2) => t1.Concat(new T[] { t2 })
得到结果集。
{1,2} {1,3} {1,4} {2,3} {2,4} {3,4}
这成为我们的新基地..
因为我们的输入长度= 2,所以我们就此止步了。
但如果长度是3,那么有了这个基础,我们会再次运行,
并找到对于基类{1,2}
中的第一个元素,输入列表中的元素大于第一个元素(2 in {1, 2})
的最后一个元素将为3和4。
所以我们会有2个元组.. {1,2,3} and {1,2,4}
同样我们会{1,3,4}
同样我们会{2,3,4}
你可以看到基数中最后一个元素为4的元素如何被消除。
这个过程重复进行,直到得到我们想要的元组大小。