Этой осенью я разработал проект для класса программирования на 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'))
Я не могу придумать, как заставить дерево работать, если я сохранил данные в файл или массив (и я не особо хочу создавать динамический массив, хотя я могу это понять, если хранить вещи в массиве). в конечном итоге работает.) Проблема в том, что я не уверен, как ссылаться на конкретный узел. Любые другие варианты или мысли о том, как с ними работать? Спасибо! (И с Рождеством!)
На самом деле boost :: serialization работает довольно хорошо, и изучение основ не так уж сложно.
Однако вы можете рассмотреть некоторые библиотеки более высокого уровня, такие как буферы протокола. Таким образом, вы можете получить базу данных, которая будет работать как для вашей версии на Python, так и для C ++.
РЕДАКТИРОВАТЬ : boost :: python не является правильным решением, поскольку позволяет только создавать привязки. Было бы очень трудно использовать его для сохранения данных.
Boost serialize позволяет сериализовать (а затем легко сохранить на диске) структуры C ++. Просто попробуйте примеры из документации.
Буферы протокола — это формат сериализации, который позволяет обмениваться данными в двоичном формате. Формат четко определен, поэтому вы можете читать / писать на разных языках и обмениваться данными.
Внутри кода проще манипулировать, как, например, XML: http://code.google.com/p/protobuf/
Тем не менее, я думаю, что это потребует немного больше усилий, чем boost :: serialize. Любые способы, оба стоит изучить, и будут полезны для дальнейших проектов.
Есть библиотека C ++ с открытым исходным кодом на http://www.picklingtools.com
это позволяет вам выбирать объекты в C ++ (возможно, позволяя вам повторно использовать ваши
маринованные файлы из вашего проекта Python). Лучше всего для сериализации стандарта
Структуры данных Python (dict, list, кортежи, int, float и т. Д.), Но могут работать
с классами с некоторой работой (в конце концов, классы Python обычно реализуются с использованием
диктовать пространство имен).
Простой пример, показывающий, как использовать библиотеку 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 ++ выглядеть и
чувствую то же самое, и использовать рассол для общения, но это сложнее, чем кажется
(отчасти потому, что в C ++ нет реального представления о самоанализе во время выполнения (оно ограничено
RTTI)), так что вы склонны описывать весь ваш класс C ++ в терминах кортежей,
словари, списки вручную. А затем зарегистрируйте эти функции с помощью загрузчика солений.
Это можно сделать, но учитывая простую природу вашей проблемы, я бы придерживался
структуры данных Python (списки, разметки, кортежи, целые числа, числа с плавающей запятой, строки, нет)
и использовать их для представления дерева 20 вопросов.