【问题标题】:How to find all partitions of a set (C++) [duplicate]如何查找集合的所有分区(C++)[重复]
【发布时间】:2020-05-14 10:56:33
【问题描述】:

我在 C# 中有以下代码来计算集合的分区

取自 (How to find all partitions of a set)

public static IEnumerable<List<List<T>>> GetAllPartitions<T>(T[] elements) {
    var lists = new List<List<T>>();
    var indexes = new int[elements.Length];
    lists.Add(new List<T>());
    lists[0].AddRange(elements);
    for (;;) {
        yield return lists;
        int i,index;
        for (i=indexes.Length-1;; --i) {
            if (i<=0)
                yield break;
            index = indexes[i];
            lists[index].RemoveAt(lists[index].Count-1);
            if (lists[index].Count>0)
                break;
            lists.RemoveAt(index);
        }
        ++index;
        if (index >= lists.Count)
            lists.Add(new List<T>());
        for (;i<indexes.Length;++i) {
            indexes[i]=index;
            lists[index].Add(elements[i]);
            index=0;
        }
    }

我的任务是将此代码移植到 C++。不幸的是,yield 关键字让我失望了。

在此部分:

 for (;;) {
        yield return lists; 

这里发生了什么?如果我删除 yield 关键字,代码将不起作用。这段代码也不是递归的,所以我不知道这里发生了什么

编辑:

好的,我将它移植到 C++。谢谢大家:

std::vector<std::vector<std::vector<int>>> getPartitions(const std::vector<int>& elements){
    std::vector<std::vector<std::vector<int>>> fList;

    std::vector<std::vector<int>> lists;
    std::vector<int> indexes(elements.size(), 0); // Allocate?
    lists.emplace_back(std::vector<int>());
    lists[0].insert(lists[0].end(), elements.begin(), elements.end());

    int counter = -1;

    for(;;){
        counter += 1;
        fList.emplace_back(lists);

        int i,index;
        bool obreak = false;
        for (i=indexes.size()-1;; --i) {
            if (i<=0){
                obreak = true;
                break;
            }
            index = indexes[i];
            lists[index].erase(lists[index].begin() + lists[index].size()-1);
            if (lists[index].size()>0)
                break;
            lists.erase(lists.begin() + index);
        }
        if(obreak) break;

        ++index;
        if (index >= lists.size())
            lists.emplace_back(std::vector<int>());
        for (;i<indexes.size();++i) {
            indexes[i]=index;
            lists[index].emplace_back(elements[i]);
            index=0;
        }

    }


    return fList;
}

int main()
{
    std::vector<int> elements = {0,1,2,3,4,5};
    auto fLists = getPartitions(elements);

    for(auto& lists : fLists){
        for(auto& l : lists){
            std::cout << "(";
            for(auto& e : l){
                std::cout << e << " ";
            }
            std::cout << ") ";
        }
        std::cout << std::endl;
        std::cout << "--" << std::endl;
    }

    return 0;
}

【问题讨论】:

  • 这不是我所说的好 C# 代码(我对那个 for 循环一点也不满意)。收益返回允许一次计算 IEnumerable 中的单个项目。你应该可以从微软docs.microsoft.com/en-us/dotnet/csharp/language-reference/…了解你需要的东西
  • 好吧,我想是时候咬紧牙关,在这种情况下理解收益率了。从未想过将一个函数移植到 C++ 会导致一整天的工作
  • 可能最令人困惑的部分是内容不代表一个函数,它们代表一个类。函数的所有内部状态最终都在一个隐藏类中,该类跟踪它在执行中的位置和所有变量。 yield return 的强大功能使得在没有它的语言中进行模拟需要做很多工作。
  • @raaj 查看 co_yield 表达式,它是 C++20 规范的一部分。

标签: c# c++


【解决方案1】:

长话短说,为返回的列表引入一个变量“results”,并在函数开始时对其进行初始化。然后将yield return x翻译成results.Add(x),将yield break翻译成return result。

可能有其他方法可以在不占用内存来存储结果的情况下做到这一点,但肯定很难。就我个人而言,我喜欢收益率回报的令人费解的品质。简明扼要地解释起来并不容易,但它有点像暂停正在执行的方法,同时您在堆栈上下拉以继续执行调用函数。当调用函数请求迭代中的下一项时,您将在 yield return 之后的下一条语句处继续。我相信编译器的人会对这样的解释感到不满,但它对我有用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多