2009-10-23 2 views
1

Я работаю над несколько сложным математическим кодом, написанным на C++. Я использую (templated) древовидные структуры для представления адаптивной функции. Из-за некоторых математических свойств я оказываюсь в ситуации, когда мне нужно перейти от одного типа узла к другому. Это должно происходить прозрачно и с минимальными издержками, как с точки зрения хранения, так и с точки зрения производительности, поскольку эти структуры используются в очень тяжелых вычислениях.Двоичное дерево с разными типами узлов

Детальная ситуация такова: у меня есть шаблонный абстрактный базовый класс, определяющий общие математические и структурные свойства общего, двусвязного узла. Каждому узлу нужна информация как от его родителя, так и от класса дерева верхнего уровня, а также для отслеживания его детей. Два класса наследуют от этого класса, FunctionNode и GenNode. Эти классы очень различаются с точки зрения хранения и функциональности, и не должны быть (по крайней мере, публичными) предками друг друга. Таким образом, я хотел бы построить дерево так:

 T 
    N 
    /\ 
    N N 
    /\ 
     G N 
    /\ 
    G G 

где Т представляет собой дерево, Н является нормальным FunctionNode и G является GenNode. Проблема заключается в N-G-переходе: N должен иметь детей типа G, а G - родительский тип N. Так как N и G - только кузены, а не братья и сестры, я не могу преобразовать N * в G *. Достаточно, чтобы G знал, что N является BaseNode, но N должен каким-то образом хранить G полиморфно, чтобы правильные виртуальные машины вызывались автоматически при прохождении дерева. Любые идеи о том, как решить эту проблему элегантно и эффективно, будут высоко оценены! :) Конечно, можно просто взломать это, но так как это очень фундаментальный кусок кода, я бы хотел иметь хорошее решение для него. Вполне вероятно, что в будущем будет много выводов этого кода.

С наилучшими пожеланиями,

Jonas Juselius

центр теоретической и вычислительной химии, Университет Тромсё

ответ

5

Не использовать наследование, когда делегация будет делать. Посмотрите на Стратегия шаблон дизайна для руководства по этому вопросу.

Переход «N-G» может быть лучше обработан с помощью подкласса N (N_g), который является унарным оператором (где другие N являются двоичными) и делегирует работу связанному объекту G. Поддерево G тогда - фактически - непересекающееся семейство классов, основанных на G, а не N.

T 
    N 
/\ 
N N 
    /\ 
    N_g N 
    | 
    G 
/\ 
    G G 

«Одна из проблем состоит в том, что я заранее не знаю, будет ли следующий N быть N или N_g.»

"заранее?" До чего? Если вы создаете N, а затем пытаетесь решить, должны ли они быть N_g, вы опустили несколько вещей.

  1. Вы создали экземпляр N слишком рано в процессе.

  2. Вы забыли написать конструктор N_g, который работает путем копирования Н.

  3. Вы забыли написать replace_N_with_Ng метод, который «клоны» N, чтобы создать N_g, а затем заменяет оригинал N в дереве с N_g.

Пункт полиморфизма заключается в том, что вам не нужно знать «заранее», что-то есть. Вы должны подождать как можно дольше, чтобы создать N или N_g и связать полученный N (или подкласс N) объекта с деревом.

«Кроме того, иногда мне нужно обрезать все G: s и генерировать больше N: s, прежде чем, возможно, создать еще несколько G: s."

Изобразительное. Вы идете по дереву, заменяя N_g экземпляры N экземплярами на «обрезку». Вы идете по дереву, заменяя N экземпляров на N_g, чтобы создать новое/другое поддерево G.

+0

Спасибо, это одно из решений, которые я рассмотрел. Одна из проблем заключается в том, что я не знаю заранее, будет ли следующий N N или N_g. Кроме того, иногда мне нужно обрезать все G: s и генерировать больше N: s, прежде чем, возможно, создать еще несколько G: s. –

+0

Когда я создаю N, я понятия не имею, будет ли он листовым узлом или нет. Создано N, на нее проецируется функция с использованием квадратуры, и выполняется серия линейных преобразований, которая в конце решает, является ли N листом или нет. Я назвал дерево двоичным, но это дерево тензорного пространства, что означает, что количество операций и потребности в памяти быстро взорвались в вашем лице, поэтому я должен быть осторожным. Я очень ценю ваши материалы, и я изучаю ваши идеи :) Я могу немного поработать во время строительства, но доступ/использование должно быть смазанным жиром. –

+0

Замена листа-N: s с помощью N_g: s - это, пожалуй, очень хорошее решение, так как в итоге у него должно быть очень мало штрафа за производительность. –

0

Возможно, вы используете Boost.Any?

По-моему, пример учебника на мой взгляд.

+0

Я посмотрел на него, но я немного волнуюсь, как это повлияет на производительность. Мне нужно посмотреть на это более подробно. –

0

Подумав о проблеме еще немного я придумал следующую мысль:

Логически, но не функционально, GenNode а-добрейший из FunctionNode. Если один из них разделяет FunctionNode на два класса, один из которых содержит общие знаменатели, а один, имеющий дополнительную функциональность, только FunctionNode должен иметь, FunctionNode может наследовать от этого класса, используя частное наследование. Теперь GenNode может надежно наследовать от FunctionNode, и все проблемы могут быть решены, как обычно, с помощью виртуальных машин. Любые комментарии?

Смежные вопросы