C# 如何使用收益率返回和递归获得每个字母组合?

C# 如何使用收益率返回和递归获得每个字母组合?,c#,recursion,string-concatenation,yield-return,C#,Recursion,String Concatenation,Yield Return,我有几个类似的字符串列表,从几十个可能的列表中: 1: { "A", "B", "C" } 2: { "1", "2", "3" } 3: { "D", "E", "F" } 这三个列表仅作为示例,用户可以从几十个具有不同元素数量的类似列表中进行选择。例如,对于用户来说,这也是一个完全有效的选择: 25: { } // empty 4: { "%", "!", "$", "@" } 16: { "I", "a", "b", "Y" } 8: { ")", "z", "!", "8" }

我有几个类似的字符串列表,从几十个可能的列表中:

1: { "A", "B", "C" }
2: { "1", "2", "3" }
3: { "D", "E", "F" }
这三个列表仅作为示例,用户可以从几十个具有不同元素数量的类似列表中进行选择。例如,对于用户来说,这也是一个完全有效的选择:

25: { } // empty
 4: { "%", "!", "$", "@" }
16: { "I", "a", "b", "Y" }
 8: { ")", "z", "!", "8" }
我想做的是在保持列表的“顺序”的同时,获得所有可能的字符串组合。换句话说,假设我们正在查看第一个列表,第一个组合将是
A1D
,然后是
A1E
,然后是
A1F
,然后是
B1D
,然后是
B1E
,等等。到目前为止,我已经编写了这个递归算法:

public void Tester()
{
    var 2dList = new List { list1, list2, list3 };
    var answer = ReturnString(2dList).ToList();

    answer.ForEach(Console.WriteLine);
}

public IEnumerable<string> ReturnString(List<List<string>> list)
{
    if (!list.Any())
    {
        yield return null;
    }
    else
    {
        // for each letter in the top-most list...
        foreach (var letter in list.First())
        {
            // get the remaining lists minus the first one
            var nextList = list.Where(x => x != list.First()).ToList();

            // get the letter and recurse down to find the next
            yield return letter + ReturnString(nextList);
        }
    }
}
public void Tester()
{
var 2dList=新列表{list1,list2,list3};
var answer=ReturnString(2dList.ToList();
应答.ForEach(控制台.写线);
}
公共IEnumerable返回字符串(列表)
{
如果(!list.Any())
{
收益返回空;
}
其他的
{
//对于最上面列表中的每个字母。。。
foreach(list.First()中的变量字母)
{
//获取剩余列表减去第一个列表
var nextList=list.Where(x=>x!=list.First()).ToList();
//获取该字母并递归查找下一个字母
收益返回字母+返回字符串(nextList);
}
}
}
然而,我得到的回报是:

AStringGeneration.StringGenerator+<ReturnString>d__11
BStringGeneration.StringGenerator+<ReturnString>d__11
CStringGeneration.StringGenerator+<ReturnString>d__11
AStringGeneration.StringGenerator+d_u11
BStringGeneration.StringGenerator+d_u11
CStringGeneration.StringGenerator+d_u11

StringGeneration
ReturnString
所在的类的名称。当我在
yield return letter+…
行上放置一个断点时,它似乎在
a
B
C
之间迭代,但实际上并不递归。我不知道这里发生了什么。谁能解释一下我的算法有什么问题吗?

您需要枚举迭代器:

foreach(string s in ReturnString(...)) {
    Console.WriteLine(s);
}
这也适用于每次迭代:

foreach(string tail in ReturnString(nextList))
    yield return letter + tail;
另外,我想你可以在这里用SelectMany做点什么

from x in l1
from y in l2
from z in l3
select x + y + + z
更新:

这是一个任意版本的概要。我稍后会详细填写

private bool m_beforeStart;
private IList<IEnumerable<char>> m_lists;
private Stack<IEnumerator<char>> m_enumerators;

public bool MoveNext() {
    while (CurrentEnumerator != null && !CurrentEnumerator.MoveNext()) {
        RemoveLastChar(m_stringBuilder);
        PopEnumerator();
     }
     if (CurrentEnumerator == null && ! m_beforeStart) {
         return false;
     }
     m_beforeStart = false;
     while (PushEnumerator()) {
          if (!CurrenEnumerator.MoveNext()) {
              ClearEnumerators();
              return false;
          }
          m_stringBuilder.Append(
              m_currentEnumerator.Current
          );
      }
      return true;
}

public string Current {
    get {
        return m_stringBuilder.ToString();
    }
}

private IEnumerator<char> CurrentEnumerator {
    get {
        return m_enumerators.Count != 0 ? m_enumerators.Peek() : null;
    }
}

private void PopEnumerator() {
    if (m_enumerators.Count != 0) {
        m_enumerators.Pop();
    }
}

private bool PushEnumerator() {
    if (m_enumerators.Count == m_lists.Count) {
        return false;
    }
    m_enumerators.Push(m_lists[m_enumerators.Count].GetEnumerator());
}
private bool m__beforeStart;
私有IList m_列表;
私有堆栈m_枚举器;
公共图书馆{
while(CurrentEnumerator!=null&&!CurrentEnumerator.MoveNext()){
RemoveLastChar(m_stringBuilder);
PopEnumerator();
}
if(CurrentEnumerator==null&&!m_beforeStart){
返回false;
}
m_beforeStart=假;
while(PushEnumerator()){
如果(!CurrenEnumerator.MoveNext()){
ClearEnumerators();
返回false;
}
m_stringBuilder.Append(
m_currentEnumerator.Current
);
}
返回true;
}
公共字符串电流{
得到{
返回m_stringBuilder.ToString();
}
}
私有IEnumerator当前枚举器{
得到{
返回m_enumerators.Count!=0?m_enumerators.Peek():null;
}
}
私有void PopEnumerator(){
if(m_enumerators.Count!=0){
m_enumerators.Pop();
}
}
私有布尔PushEnumerator(){
if(m_enumerators.Count==m_lists.Count){
返回false;
}
m_枚举数.Push(m_列出[m_枚举数.Count].GetEnumerator());
}
公共静态IEnumerable返回字符串(IEnumerable矩阵)
{
if(matrix.Count()==1)
返回矩阵。First();
从矩阵中的字母返回。First()//第一个列表中的每个字母
let tail=matrix.Skip(1)//获取尾部列表
让tailStrings=ReturnString(tail)//递归地为每个tail构建结束列表
从尾字符串中结束//在这些尾字符串中结束每个字符串
选择字母+结尾;//将第一个列表中的字母附加到结尾
}

调用
ReturnString(lst.Where(l=>l.Any())
跳过空序列。

哎呀,我忘了提到我得到的输出正是使用代码枚举
ReturnString
结果时显示的输出(应该是复数)。同样有趣的是,当在
收益率返回字母+…
行上设置断点时,它似乎会在
a
B
C
之间迭代,但实际上不会递归到下一个级别。谢谢,这似乎做到了!现在我只需要找出原因:)。我对任何可能的
感兴趣,请为此问题选择许多
解决方案。我试图了解它是如何工作的,但除了一些基本的情况外,目前我还无法理解。假设你知道它的深度,虽然很抱歉,我应该提到,用户可以选择的列表将是动态的。对于这种情况,收益率不是最好的选择。它不能很好地处理递归。不过,您可以使用手工制作的IEnumerator实现。您可以使用yield,但您需要一个堆栈来维护自己的激活记录。顺便说一句,他有字符串列表,而不是chars.list。其中(x=>x!=list.First()).ToList();可能会被列表替换。跳过(1)@Grozz谢谢!我知道必须有一种更简单的方法:)。您的代码工作得很好,生成的输出与我更新的代码相同。然而,我很难理解它的实际工作原理,如果要求我重写它,我将无法。他可以将
返回矩阵。首先()
,而不是将
矩阵。Count()==1
改为
矩阵。Count()==0
,并将其改为
返回新列表()
。修复了它以处理空列表,这比那要复杂一点,顺便说一句,将matrix.SkipWhile(lst=>!lst.Any())输入到第一个输入将比在函数fyi中输入更清楚、更有效:字符串串联为运行时复杂性增加了一个额外的O(N^2)项。您可能需要考虑使用链表来代替,然后使用
public static IEnumerable<string> ReturnString(IEnumerable<IEnumerable<string>> matrix)
{
    if (matrix.Count() == 1)
        return matrix.First();

    return from letter in matrix.First()    // foreach letter in first list
           let tail = matrix.Skip(1)        // get tail lists
           let tailStrings = ReturnString(tail)   // recursively build lists of endings for each tail
           from ending in tailStrings       // foreach string in these tail endings
           select letter + ending;          // append letter from the first list to ending
}