我正在Unity3D项目中使用C#脚本,我正在尝试获取字符串列表并获取排列的2D列表。以下列方式使用this answer's GetPermutations()
:
List<string> ingredientList = new List<string>(new string[] { "ingredient1", "ingredient2", "ingredient3" });
List<List<string>> permutationLists = GetPermutations(ingredientList, ingredientList.Count);
但它会引发隐式转换错误:
IEnumerable<IEnumerable<string>> to List<List<string>> ... An explicit conversion exists (are you missing a cast)?
所以我查看了一些地方,例如here,并提出了以下修改:
List<List<string>> permutationLists = GetPermutations(ingredientList, ingredientList.Count).Cast<List<string>>().ToList();
但它在运行时中断,在内部处理,并允许它继续而不指示失败 - 可能是因为它在Unity3D中运行。 这是我在停止调试脚本后在Unity3D中看到的内容:
InvalidCastException: Cannot cast from source type to destination type.
System.Linq.Enumerable+<CreateCastIterator>c__Iterator0`1[System.Collections.Generic.List`1[System.String]].MoveNext ()
System.Collections.Generic.List`1[System.Collections.Generic.List`1[System.String]].AddEnumerable (IEnumerable`1 enumerable) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Collections.Generic/List.cs:128)
System.Collections.Generic.List`1[System.Collections.Generic.List`1[System.String]]..ctor (IEnumerable`1 collection) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Collections.Generic/List.cs:65)
System.Linq.Enumerable.ToList[List`1] (IEnumerable`1 source)
我认为这仍然是错误的,所以我也尝试了以下方法以及更多我不记得的方法:
List<List<string>> permutationLists = GetPermutations(ingredientList, ingredientList.Count).Cast<List<List<string>>>();
List<List<string>> permutationLists = GetPermutations(ingredientList.AsEnumerable(), ingredientList.Count);
以及在方法调用之前使用括号显式转换,就像在C或Java中一样,仍然无济于事。
那么我应该如何将GetPermutations()
函数的结果投射到List<List<string>>
?或者,我怎么能修改函数只返回List<List<string>>
,因为我不需要它为泛型类型工作?我试着自己修改方法如下:
List<List<string>> GetPermutations(List<string> items, int count)
{
int i = 0;
foreach(var item in items)
{
if(count == 1)
yield return new string[] { item };
else
{
foreach(var result in GetPermutations(items.Skip(i + 1), count - 1))
yield return new string[] { item }.Concat(result);
}
++i;
}
}
但是,从函数名中删除<T>
后,它会断言主体不能是迭代器块。我之前没有使用C#的经验,而且我在强类型语言中生成了模板函数,所以任何解释/帮助都是值得赞赏的。
我不确定如何看待这个问题,所以如果这是重复的,请在此处发布,我会立即删除此帖。
答案 0 :(得分:8)
那么我应该如何从GetPermutations()函数中转换结果来获取
List<List<string>>
最佳解决方案:不要。为什么需要首先将序列转换为列表?将其保留为一系列序列。
如果你必须:
GetPermutations(...).Select(s => s.ToList()).ToList()
如果你想修改原始方法,只需做同样的事情:
IEnumerable<List<string>> GetPermutations(List<string> items, int count)
{
int i = 0;
foreach(var item in items)
{
if(count == 1)
yield return new List<T>() { item };
else
{
foreach(var result in GetPermutations(items.Skip(i + 1), count - 1))
yield return (new string[] {item}.Concat(result)).ToList();
}
++i;
}
}
然后执行GetPermutations(whatever).ToList()
并列出一系列列表。但同样,不要这样做。如果可能,请将所有内容保留在序列中。
我想将序列转换为列表,以便我可以按字母顺序对元素进行排序,并将它们重新连接为有序的单个逗号分隔字符串。
好的,然后那样做。让我们将您的方法重写为扩展方法Permute()
。让我们制作一些新的单线方法:
static public string CommaSeparate(this IEnumerable<string> items) =>
string.Join(",", items);
static public string WithNewLines(this IEnumerable<string> items) =>
string.Join("\n", items);
static public IEnumerable<string> StringSort(this IEnumerable<string> items) =>
items.OrderBy(s => s);
然后我们有以下内容 - 我将按照我们的方式注释类型:
string result =
ingredients // List<string>
.Permute() // IEnumerable<IEnumerable<string>>
.Select(p => p.StringSort()) // IEnumerable<IEnumerable<string>>
.Select(p => p.CommaSeparate())// IEnumerable<string>
.WithNewLines(); // string
我们已经完成了。 看看当你制作一个做得好但做得好的方法时,代码是多么清晰明了。看看按顺序保存所有内容是多么容易。
答案 1 :(得分:1)
您的问题与C#和.net类型系统的几个方面有关。我将尝试提供简单的解释,并提供链接作为更正式的答案。
因此,根据您的描述,procedure TForm1.btn1Click(Sender: TObject);
begin
AnimateWindow(Panel1.Handle, 800, AW_SLIDE or AW_VER_NEGATIVE or AW_HIDE);
end;
procedure TForm1.btn2Click(Sender: TObject);
begin
AnimateWindow(Panel1.Handle, 800, AW_SLIDE or AW_VER_POSITIVE or AW_ACTIVATE);
end;
看起来像GetPermutations(ingredientList, ingredientList.Count);
,但您尝试将此结果分配给另一种类型的变量,伪代码:
IEnumerable<IEnumerable<string>>
List<List<string>> = IEnumerable<IEnumerable<string>>;
实现List<T>
,因此通常可以进行此分配:
IEnumerable<T>
但问题是,在您的情况下,左侧的IEnumerable<T> = List<T>;
与右侧的T
不同。
T
IEnumerable<IEnumerable<string>>
为T
。
{li> for IEnumerable<string>
List<List<string>>
为T
要解决您的问题,我们应该更改代码,使其在左侧和右侧具有相同的List<string>
,即将T
转换为T
或List<string>
。
您可以通过以下方式将IEnumerable<string>
转换为T
:
List<string>
但一般来说,在所有地方使用List并不是一个好主意。仅在您真正需要的地方使用列表。重点:
IEnumerable<List<string> GetPermutationsList(List<string> items, int count)
{
return GetPermutations(items, count).Select(x=>x.ToList())
}
IEnumerable<List<string>> permutationLists = GetPermutations(ingredientList.AsEnumerable(), ingredientList.Count);
// or
List<List<string>> permutationLists = GetPermutations(ingredientList.AsEnumerable(), ingredientList.Count).ToList();
提供了最低限度的功能(仅限枚举),应该符合您的目标。IEnumerable<T>
(List实现它)提供最大功能(按索引添加,删除,“随机”访问)。你真的需要最大的功能吗?IList <T>
会导致大数据的内存不足问题。ToList()
只是强制立即进行查询评估并返回ToList()
一些有用的信息:covariance-contr-variance,List,casting