2

今年秋天,我用 Python 为编程课做了一个项目,有一个 20 题风格的游戏,可以从用户的反应中学习。它使用基于对问题的是/否响应的树,并为每个决定以及动物在到达“分支”末端时提出独特的问题。

在课程快结束时,我们在 C++ 中做了一些工作(但我对它还是很陌生),我想在我的休息时间制作一个 C++ 版本的项目——这将使它更容易运行例如,一个可执行文件。但是,我发现 C++ 没有太多用于 pickle 式数据存储的选项,而且我认为 Boost.serialization 或 Boost.Python 不会特别适合这种情况。

还有其他选择吗,或者您对如何在 C++ 中以另一种方式处理数据有什么建议?

原始的 Python 代码包括:

    def check(self):
        correct, lastnode = self.ask(self.root) #lastnode is the closest guess to the new animal
        if correct =='n':
            print("Rats! I didn't get it. Please help me improve.")
            newanimal = AnTreeNode(input("What is your animal? "))
            oldanimal = lastnode
            newquestion = input("Please enter a yes/no question that would\n select between a(n) %s \
and a(n) %s: " %(newanimal,lastnode.data))+" "
            direction = input("What would be the correct answer for a(n) %s? " %newanimal)
            newnode = AnTreeNode(newquestion, parent = lastnode.parent)
            if lastnode.parent == None:
                self.root = newnode
            elif lastnode.parent.yes == lastnode:
                newnode.parent.yes = newnode
            else:
                newnode.parent.no = newnode
            if direction == 'y':
                newnode.yes, newnode.no = newanimal, oldanimal
            elif direction == 'n':
                newnode.yes, newnode.no = oldanimal, newanimal
            newanimal.parent = newnode
            oldanimal.parent = newnode
            self.dumpTree()
        elif correct == 'y':
            print("I am soooo smart!")

    def loadTree(self):
        try:
            f = open(self.treefile, "rb")
            self.root = pickle.load(f)
        except:
            self.root = AnTreeNode("frog")  

    def dumpTree(self):
        pickle.dump(self.root, open(self.treefile, 'wb'))

如果我将数据保存到文件或数组中,我想不出一种使树工作的方法(而且我并不特别想制作一个动态数组,尽管如果将内容存储在数组中我可以弄清楚最终工作了。)这些问题是我不确定如何引用特定节点。任何其他选项,或关于如何使用这些选项的想法?谢谢!(还有圣诞快乐!)

4

2 回答 2

3

实际上 boost::serialization 效果很好,学习基础也不是那么难。

但是,您可能会考虑一些更高级别的库,例如协议缓冲区。这样,您就可以拥有适用于您的 python 和 C++ 版本的数据库。

编辑: boost::python 不是正确的解决方案,因为它只允许进行绑定。用它来保存数据真的很痛苦。

Boost serialize 允许序列化(然后轻松地保存在磁盘上)C++ 结构。只需尝试文档中的示例。

协议缓冲区是一种序列化格式,允许以二进制格式交换数据。格式定义明确,因此您可以从不同的语言读取/写入并交换数据。在代码内部操作更容易,例如 XML:http ://code.google.com/p/protobuf/ 但是,我认为它需要比 boost::serialize 更多的努力。任何方式,都值得学习,并且对进一步的项目有用。

于 2012-12-23T22:39:41.430 回答
0

http://www.picklingtools.com上有一个开源 C++ 库 ,允许您在 C++ 中腌制对象(可能允许您重新使用 Python 项目中的腌制文件)。它最适合序列化标准 Python 数据结构(dict、list、tuples、ints、float 等),但也可以处理一些需要工作的类(毕竟,Python 类通常使用 dict 作为命名空间来实现)。

一个简单的例子,展示了如何使用 picklingtools 库(并让它与 C++ 和 Python 一起工作)

#include "chooseser.h"
#include <iostream>

int main (int argc, char **argv)
{
   // Create a Python style dictionary
   Val v = Tab("{'a':1, 'b':2.2, 'c':'three', 'nested':{ 'zz':1 }");
   v["d"] = "something";

   // print out a nested key
   cout << v["nested"]["zz"] << endl;  

   // Pickle the data structure to disk
   DumpValToFile(v, "state.p0", SERIALIZE_P2);

//  # Python side would read this same file with
// >>> import cPickle
// >>> result = cPickle.load( file('state.p0') ) # load fiigures out which prototcol

}

我可能会使用字典或列表来构建你的树:

questions_dict = { 'data': 'COW', 'left': None, 'right': { ''data':'CAT', left': None, 'right': None } }
print questions_dict['right']['data'] # Right child's data

LEFT = 1; RIGHT = 2; DATA = 0
questions_list = ["COW", None, ["CAT", None, None] ]
print questions_list[RIGHT][DATA]  # Right child's data

有了它,您可以在 C++ 和 Python 之间来回切换字典或列表,然后做您需要的事情。您可以按原样使用所有内容。

在文档选项卡下的网站上有关于如何在 C++ 中腌制和取消腌制的重要文档。开始的最佳位置可能是 http://www.picklingtools.com/documentation用户指南

警告!前方危险! 您可以使用 C++ 序列化构建类(因此您在 Python 中的相同类将具有 C++ 等价物),但这很重要;y 更困难:如果您可以保持在标准 Python 数据结构中,您的生活会容易得多。为此,您必须在系统中注册一个带有其构造函数的 C++ 类。您必须具有构建功能(请参见下面的简单示例):

// When registering things with the factory, they take in some tuple
// and return a Val: REDUCE tend to be more for built-in complicated
// types like Numeric, array and complex.  BUILD tends to more for
// user-defined types.
typedef void (*FactoryFunction)(const Val& name, 
                                const Val& input_tuple, 
                                Val& environment,  
                                Val& output_result);


// Example of how user-defined classes would probably serialize: the
// user-defined classes tend to be built with BUILD instead of REDUCE,
// which passes slightly different arguments to the tuple.  As an
// example, magine a simple Python class:
//
//   class Scott(object) :
//     def __init__(self, n) : self.data = int(n)
//
// A dumps(a,2) (where a= Scott(100)) looks like
// '\x80\x02c__main__\nScott\nq\x01)\x81q\x02}q\x03U\x04dataq\x04Kdsb.'/
//
// type_object: a tuple (string_name, args). i.e., ('__main__\nScott\n', ()) 
// input: a dictionary                       i.e., {'data', 100 }
inline void BuildScottFactoryFunction (const Val& type_object, 
                       const Val& input,
                       Val& /* environment */,
                       Val& output_result)
{
  cout << "SCOTT:type_object:" << type_object << endl;
  cout << "SCOTT:input:" << input << endl;

  string name = type_object(0);
  Tup&   args = type_object(1);
  cout << "name:" << name << " args:" << args << endl;

  if (name!="__main__\nScott\n") throw runtime_error("Not right name");
  Val& result = input("data");
  output_result = result;
}

然后你必须在你自己的 PickleLoader 中注册这个类。

PickleLoader p(buffer, buffer_len);
p.registerFactory("__main__\nScott\n", BuildScottFactoryFunction);

你可以让它工作,这样你就可以让 Python 和 C++ 类看起来和感觉一样,并使用 pickle 进行通信,但它比看起来更难(部分原因是 C++ 没有真正的运行时自省概念(它仅限于 RTTI )),所以你往往不得不手动地用元组、字典、列表的术语来描述你的整个 C++ 类。然后用 pickle loader 注册这些函数。可以做到,但考虑到您的问题的简单性质,我会坚持使用现有的 Python 数据结构(列表、字典、元组、整数、浮点数、字符串、无)并使用它们来表示您的 20 个问题树。

于 2014-07-16T15:32:12.153 回答