【问题标题】:How to hack an R-style list in C++?如何破解 C++ 中的 R 样式列表?
【发布时间】:2015-05-10 22:58:49
【问题描述】:

我有一个问题,我想创建一个类似于 C++ 中 R 样式列表的数据结构。该结构类似于可以容纳可变大小行的矩阵。这是一个 C++ 任务,所以我实际上不能在 R 本身中执行此操作。

我是这样想的:

  • 列表的每个“级别”都是 char 的 C++ 向量
  • 有一个指针向量,每个指针指向列表的不同级别。

我知道如何单独创建每个级别向量,但不知道如何将它们连接到指针向量以便可以访问它们。此外,我将使用什么 C++ 语法来访问关卡中的特定元素,例如如果我想访问第二层的第三个元素,在 R 中它会类似于 Levels[[2]]$elements[3]。这是如何在 C++ 中完成的?

【问题讨论】:

  • 这将是学习 C++ 的痛苦方式。买一本 C++ 书,学习这门语言。然后,您将更好地理解用一种语言表达的概念如何用另一种语言表达。
  • 怎么样...#include <vector> typedef std::vector<char> Row_t; typedef std::vector<Row_t> RLikeSomething_t;?您可以像这样访问:RLikeSomething_t something; .... something[row][column] = 'x';

标签: c++ r list vector


【解决方案1】:

这是我使用 Rcpp 的示例只是为了简化示例的编译:所有数据类型都是 STL 类型(尽管我们使用隐式转换来获取列表):

#include <Rcpp.h>

// [[Rcpp::export]]

std::list<std::vector<double> > ex(std::vector<double> a,
                                   std::vector<double> b) {

  std::list<std::vector<double> > l;
  l.push_back(a);
  l.push_back(b);

  return l;
}

/*** R
v <- sqrt(1:5)
w <- log(3:5)
ex(v, w)
***/

您可以将其直接导入 R 以将其 编译,然后 链接 和 加载 以及最后的示例 运行:

R> Rcpp::sourceCpp("/tmp/ex.cpp")

R> v <- sqrt(1:5)

R> w <- log(3:5)

R> ex(v, w)
[[1]]
[1] 1.00000 1.41421 1.73205 2.00000 2.23607

[[2]]
[1] 1.09861 1.38629 1.60944

R> 

所以这应该满足您的要求,因为列表容器包含两个不同长度的向量。

您可能会发现Rcpp documentation 有助于在 R 和 C++ 之间来回切换 --- 但我赞同 DavidO 的评论,即您还需要学习 C++ 本身。

【讨论】:

    【解决方案2】:

    为了完成任务,模拟一堆积木的堆叠和拆垛(谢天谢地,仅限 2D),这是我最终想出的(见下文)。堆的级别由字符向量表示,而堆的不同级别由指向 PileLevels 的指针向量处理。我花了一些时间,但我终于明白,可以像任何其他数据类型一样将指针推送到容器类上。

    第一个类来定义和处理一堆堆上的操作:

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    class PileLevel
    {
    
    public:
        vector<char> elements;
    
        char GetLevelElement(int i);
        void SetLevelElement(int i, char newelt);
        void PushLevelElement(char newelt);
        char PopLevelElement();
        int GetLevelSize();
        void PrintLevel();
        void ClearLevel();
        bool isLevelEmpty();
    };
    
    char PileLevel::GetLevelElement(int i)
    {
        return elements.at(i);
    }
    
    void PileLevel::SetLevelElement(int i, char newelt)
    {
        elements.at(i) = newelt;
    }
    
    void PileLevel::PushLevelElement(char newelt)
    {
        elements.push_back(newelt);
    }
    
    int PileLevel::GetLevelSize()
    {
        return elements.size();
    }
    
    char PileLevel::PopLevelElement()
    {
        int lst = GetLevelSize();
        //cout << lst << endl;
        char eltToRet = elements[lst-1];
        //cout << eltToRet << endl;
        elements.pop_back();
        return eltToRet;
    }
    
    void PileLevel::PrintLevel()
    {
        int ctr=0;
        for (ctr = 0; ctr < GetLevelSize(); ctr++)
        {
            cout << elements.at(ctr) << " " ;
        }
        cout << endl;
    }
    
    void PileLevel::ClearLevel()
    {
        elements.clear();
    }
    
    bool PileLevel::isLevelEmpty()
    {
        if (elements.size() == 0)
            return true;
        else
            return false;
    }
    

    接下来,一个类来处理堆的操作:

    #include <iostream>
    #include <vector>
    
    #include "PileLevel.h"
    
    using namespace std;
    
    class BlockPile
    {
    public:
        vector<PileLevel*> Levels;
        int PileHeight;
    
        //constructor and destructor
        BlockPile();
        ~BlockPile();
    
        //helper functions
        void PrintPile();
        int GetPileHeight();
        void AddLevel();
        void AddLevel(char newElt);
        char PopLvlElt(int lvl);
        void PopEntireLevel(int lvl);
        void DeleteTopLevel();
        bool isPileEmpty();
        void PushLvlElt(char block, int lvl);
        void TryStackOnNewTopLevel(char block);
        void TryStackOnLowerLevel(char block, int lvl);
        void unStackPile();
    
        //required functions
        void clear();
        bool isEmpty();
        char unStack();
        char topBlock();
        void stack(char block);
        int numBlocks();
    };
    
    //BlockPile::BlockPile()  //test constructor to make sure basic push capabilities are working
    //{
    //  PileLevel* base = new PileLevel;
    //  base->PushLevelElement('A');
    //  base->PushLevelElement('B');
    //  base->PushLevelElement('C');
    //  Levels.push_back(base);
    //  PileLevel* L1 = new PileLevel;
    //  L1->PushLevelElement('D');
    //  L1->PushLevelElement('E');
    //  L1->PushLevelElement('F');
    //  Levels.push_back(L1);
    //}
    
    BlockPile::BlockPile()
    {
        PileLevel* base = new PileLevel;
        base->PushLevelElement('A');
        Levels.push_back(base);
    }
    
    BlockPile::~BlockPile()
    {
        while (! isPileEmpty() )
        {
            DeleteTopLevel();
        }
    }
    
    
    int BlockPile::GetPileHeight()
    {
        //cout << "Pile is:  " << Levels.size() << " levels high." << endl;
        return Levels.size();
    }
    
    void BlockPile::PrintPile()
    {
        int ctr = 0;
        int lim = GetPileHeight();
        //cout << lim << endl;
        if (! isPileEmpty())
        {
            for (ctr = 0; ctr < lim; ctr++)
            {
                Levels[ctr]->PrintLevel();
            }  
            cout << endl;
        }
        else
            cout << "Can't print empty pile." << endl;
    }
    
    void BlockPile::AddLevel()  //add an empty level
    {
        PileLevel* nuLevel = new PileLevel;
        Levels.push_back(nuLevel);
    }
    
    void BlockPile::AddLevel(char newElt)  //add a new level with one element pushed
    {
        PileLevel* nuLevel = new PileLevel;
        nuLevel->PushLevelElement(newElt);
        Levels.push_back(nuLevel);
    }
    
    void BlockPile::DeleteTopLevel()
    {
        PileLevel* delPtr;
        int ht = GetPileHeight();
    
        if (! isPileEmpty())
        {
            delPtr = Levels[ht-1];  //set a pointer to the top level 
            Levels[ht-1]->ClearLevel(); //clear any remaining data in the top level
            Levels[ht-1] = NULL; //disconnect the level from the vector of levels
            delete delPtr; //delete the level
            Levels.pop_back(); //pop the pointer to the level
        }
        else
            cout << "Pile is empty." << endl;
    }
    
    char BlockPile::PopLvlElt(int lvl)
    {   char eltToRet;
    
        eltToRet = Levels[lvl]->PopLevelElement();
        //cout << Levels[lvl]->GetLevelSize() << endl;
    
        //must take these steps to clean up the pile if element popped was the only item remaining on the level
        if (Levels[lvl]->GetLevelSize() == 0)
        {
            vector<PileLevel*>::iterator ptVecIter = Levels.begin();  //need to declare this iterator and use .begin() because .erase() is defined to take iterator arguments
            Levels.erase(Levels.begin() + (lvl));  
        }
        return eltToRet;
    }
    
    void BlockPile::PopEntireLevel(int lvl)
    {
        while (! Levels[lvl]->GetLevelSize() <= 0)
        {
            cout << Levels[lvl]->PopLevelElement() << " " ;
        }
        //must take these steps to clean up the pile if element popped was the only item remaining on the level
        if (Levels[lvl]->GetLevelSize() == 0)
        {
            vector<PileLevel*>::iterator ptVecIter = Levels.begin();  //need to declare this iterator and use .begin() because .erase() is defined to take iterator arguments
            Levels.erase(Levels.begin() + (lvl));  
        }
        cout << endl;
    }
    
    void BlockPile::PushLvlElt(char block, int lvl)
    {
        if (Levels[lvl]->GetLevelSize() >= 0) //if the current level exists and contains 0 or more elements
            Levels[lvl]->PushLevelElement(block);
        else if (Levels[lvl] == NULL) //if pointer to current level is NULL
        {
            Levels[lvl] = new PileLevel;
            Levels[lvl]->PushLevelElement(block);
        }
        else if (lvl > GetPileHeight())  //if lvl is greater than current height of pile
        {
            AddLevel(block);  //add a new level to the pile and push the block on it
        }
    }
    
    
    bool BlockPile::isPileEmpty()
    {
        if (Levels.size() == 0)
            return true;
        else
            return false;
    }
    
    void BlockPile::unStackPile()
    {
        int lim = GetPileHeight() - 1;
    
        while (lim > -1)
        {
            PopEntireLevel(lim);
            lim--;
        }
    }
    
    void BlockPile::TryStackOnNewTopLevel(char block)
    {
        int oldTopLvl = GetPileHeight()-1;  //get current top level of pile
    
        AddLevel();  //add a dummy new level but don't push anything on it yet
        int newTopLvl = GetPileHeight()-1;  //get new top level after adding dummy level
    
        int newTopLvlSize = Levels[newTopLvl]->GetLevelSize();  //should be 0
        int oldTopLvlSize = Levels[oldTopLvl]->GetLevelSize();  //should be > 0
        int sizeDiff = oldTopLvlSize - newTopLvlSize; 
    
        //cout << newTopLvlSize << "  " << oldTopLvlSize << "  " << sizeDiff << endl;
    
        if (sizeDiff >=2)  //if difference in size between dummy level and old top level is at least 2
            Levels[newTopLvl]->PushLevelElement(block);  //push the block on the new top level
        else
        {
            DeleteTopLevel();  //remove the dummy level, not ready for it yet
            TryStackOnLowerLevel(block,oldTopLvl);  //try to push the block on the next lower level 
        }
    }
    
    void BlockPile::TryStackOnLowerLevel(char block, int lvl)
    {
        if (lvl == 0)  //if at level 0, base of the pile, can't go lower than this
        {
            Levels[lvl]->PushLevelElement(block);  //must push the block at the base
        }
        else //if not yet at base
        {
            int thisLvlSize = Levels[lvl]->GetLevelSize();  //get size of current level 
            int lowerLvlSize = Levels[lvl-1]->GetLevelSize(); //get size of next lower level 
            int sizeDiff = lowerLvlSize - thisLvlSize;  //difference in size
    
            //cout << thisLvlSize << "  " << lowerLvlSize << "  " <<  sizeDiff << endl;
    
            if (sizeDiff >= 2)  //if size difference is at least 2
                Levels[lvl]->PushLevelElement(block);  //push block on current level
            else  //if diff is smaller than 2
                TryStackOnLowerLevel(block,lvl-1);  //make recursive call and try to push it on a lower level
        }
    }
    
    //required functions
    void BlockPile::clear()
    {
        int lim = GetPileHeight()-1;
    
        while (lim > -1)
        {
            DeleteTopLevel();
            lim--;
        }
    }
    
    bool BlockPile::isEmpty()
    {
        return isPileEmpty();
    }
    
    char BlockPile::unStack()
    {
        int ht = GetPileHeight()-1;
    
        return PopLvlElt(ht);
    }
    
    char BlockPile::topBlock()
    {
        int ht = GetPileHeight()-1;
    
        int lsize = Levels[ht]->GetLevelSize() - 1;
    
        return Levels[ht]->GetLevelElement(lsize);
    }
    
    void BlockPile::stack(char block)
    {
        TryStackOnNewTopLevel(block);
    }
    
    int BlockPile::numBlocks()
    {
        int acc = 0;
        int ht = GetPileHeight();
        int ctr;
    
        for(ctr = 0; ctr < ht; ctr++)
        {
            acc = acc + Levels[ctr]->GetLevelSize();
        }
        return acc;
    }
    

    最后是一个驱动程序:

    #include <iostream>
    #include <array>
    
    #include "BlockPile.h"
    
    using namespace std;
    
    int main()
    {
        //PileLevel myLevel;
    
        //myLevel.PushLevelElement('A');
        //myLevel.PushLevelElement('B');
        //myLevel.PushLevelElement('C');
        //myLevel.PushLevelElement('D');
    
        //myLevel.PrintLevel();
        //cout << myLevel.GetLevelSize() << endl;
        //myLevel.PopLevelElement();
        //myLevel.PrintLevel();
    
        //BlockPile myPile;
        //myPile.GetPileHeight();
        //myPile.PrintPile();
        //myPile.AddLevel('Z');
        //myPile.GetPileHeight();
        //myPile.PrintPile();
        //myPile.PushLvlElt('P', 2);
        //myPile.PushLvlElt('Q', 1);
        //myPile.PrintPile();
        //cout << myPile.PopLvlElt(2) << endl;
        //cout << myPile.PopLvlElt(1) << endl;
        //cout << myPile.PopLvlElt(0) << endl;
        //myPile.PrintPile();
        //myPile.DeleteTopLevel();
        ////myPile.DeleteTopLevel();
        //myPile.PrintPile();
        //myPile.PushLvlElt('G',0);
        //myPile.PushLvlElt('H',0);
        //myPile.AddLevel('I');
        //myPile.PushLvlElt('J',2);
        //myPile.AddLevel('K');
        //myPile.PrintPile();
        ////myPile.PopEntireLevel(3);
        ////myPile.PopEntireLevel(2);
        //myPile.unStackPile();
        //myPile.PrintPile();
    
        //BlockPile myPile2;
        //myPile2.PrintPile();
        //myPile2.TryStackOnNewTopLevel('B');
        //myPile2.PrintPile();
        //myPile2.TryStackOnNewTopLevel('C');
        //myPile2.PrintPile();
        //myPile2.TryStackOnNewTopLevel('D');
        //myPile2.PrintPile();
        //myPile2.TryStackOnNewTopLevel('E');
        //myPile2.PrintPile();
        //myPile2.TryStackOnNewTopLevel('F');
        //myPile2.PrintPile();
    
        array<char,44> BlockSet = {'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '+', '-', '=', '/', '<', '>', '?', ',', '.'};
        int BSSize = BlockSet.size(); 
        int idx = 0;
    
        BlockPile myPile3;
        myPile3.PrintPile();
        //myPile3.clear();
        for (idx = 0; idx < BSSize; idx++)
        {
            myPile3.stack(BlockSet[idx]);
            cout << "Number of blocks currently in pile:  " << myPile3.numBlocks() << endl;
            myPile3.PrintPile();
        }
    
        cout << "Top block is:  " << myPile3.topBlock() << endl;
        cout << endl;
    
        for (idx=0; idx < 5; idx++)
        {
            cout << "Unstacking block:  "  << myPile3.unStack() << endl;
            cout << "Top block is now:  " << myPile3.topBlock() << endl;
            cout << "Number of blocks is now:  " << myPile3.numBlocks() << endl;
            cout << endl;
        }
    
        cout << endl;
    
        cout << "Unstacking remainder of pile..." << endl;
        myPile3.unStackPile();
    
        system("pause");
        return 0;
    }
    

    瞧。

    【讨论】:

      猜你喜欢
      • 2020-05-28
      • 2011-03-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-06
      • 1970-01-01
      • 2012-08-21
      相关资源
      最近更新 更多