Skip to content

Commit

Permalink
Merge pull request #2 from yalibs/feature/parent
Browse files Browse the repository at this point in the history
parent field and a forward_iterator implementation
  • Loading branch information
sillydan1 authored Nov 2, 2022
2 parents fcd3a52 + 3249983 commit d1f4338
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 21 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ _deps

cmake-build-*
.idea/
.DS_Store
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
cmake_minimum_required(VERSION 3.0)
project(yatree VERSION 1.1.0)
project(yatree VERSION 1.2.0)
set(CMAKE_CXX_STANDARD 20)
set(CXX_STANDARD_REQUIRED ON)
include_directories(include)
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ auto my_tree = tree<std::string>{"+"}
.emplace("1")
.emplace("2");
my_tree.apply_dfs(printer_function); // + 1 2
std::cout << std::endl;

//// You can even concatenate trees together
//// *
Expand All @@ -24,5 +23,8 @@ auto my_tree2 = tree<std::string>{"*"}
.emplace("3")
.concat(my_tree);
my_tree2.apply_dfs(printer_function); // * 3 + 1 2
std::cout << std::endl;

//// trees are even forward iterable in a DFS manner as well.
for(auto& n : my_tree2)
std::cout << n.node << " "; // * 3 + 1 2
```
164 changes: 153 additions & 11 deletions include/implementation/tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,145 @@
*/
#ifndef YATREE_TREE_H
#define YATREE_TREE_H
#include <utility>
#include <vector>
#include <optional>
#include <functional> // std::reference_wrapper
#include <stack>

namespace ya {
template<typename T>
struct tree {
tree() : node{}, children{} {}
explicit tree(const T &r) : node(r), children{} {}
explicit tree(T &&r) : node{std::forward<T>(r)}, children{} {}
using children_t = std::vector<tree<T>>;
// TODO: Out of scope iterator features
// - implement swap
// - implement operator=(const _left_df_iterator&)
// - implement operator--
// - end should be indices = [ root.children.size() ] <--
struct _left_df_iterator {
explicit _left_df_iterator(tree<T>& root) : root{root}, indices{} {}
_left_df_iterator(tree<T>& root, std::vector<size_t> indices) : root{root}, indices{std::move(indices)} {}
auto operator++() -> _left_df_iterator& {
if(indices.empty()) {
if(root._children.empty())
throw std::out_of_range("root node has no children");
indices.push_back(0);
return *this;
}
if(indices[0] >= root._children.size())
throw std::out_of_range("cannot increment iterator to end");
auto& e = operator*();
if(!e.children().empty()) {
indices.push_back(0);
return *this;
}
auto parent = e.parent();
while(!indices.empty() && parent.has_value()) {
auto has_sibling = indices[indices.size()-1]+1 < parent.value()->children().size();
if(has_sibling) {
indices[indices.size()-1]++;
return *this;
}
indices.erase(indices.end()-1);
parent = operator*().parent();
}
indices.push_back(root._children.size());
return *this;
}
auto operator++(int) -> _left_df_iterator {
auto x = *this;
operator++();
return x;
}
auto operator+=(size_t index) -> _left_df_iterator& {
for(size_t i = 0; i < index; i++)
this->operator++();
return *this;
}
auto operator*() const -> tree<T>& {
tree<T>* e = &root;
for(auto& i : indices) {
e = &(e->operator[](i));
}
return *e;
}
auto operator->() const -> tree<T>* {
tree<T>* e = &root;
for(auto& i : indices) {
e = &(e->operator[](i));
}
return e;
}
auto operator==(const _left_df_iterator& other) const -> bool {
if(&root != &other.root)
return false;
if(indices.size() != other.indices.size())
return false;
for(auto i = 0; i < indices.size(); i++) {
if(indices[i] != other.indices[i])
return false;
}
return true;
}
auto operator!=(const _left_df_iterator& o) const -> bool {
return ! this->operator==(o);
}
auto operator=(const _left_df_iterator& o) -> _left_df_iterator& {
root = o.root;
indices = o.indices;
return *this;
}
private:
tree<T>& root;
std::vector<size_t> indices;
};
tree() : node{}, _children{}, p{nullptr} {}
explicit tree(const T &r) : node(r), _children{}, p{nullptr} {}
explicit tree(T &&r) : node{std::forward<T>(r)}, _children{}, p{nullptr} {}
tree(const tree<T>& o) : node{o.node}, _children{o.children()}, p{nullptr} {
for(auto& c : _children)
c.set_parent(this);
}
tree(tree<T>&& o) noexcept : node{std::move(o.node)}, _children{std::move(o._children)}, p{o.p} {}
auto operator=(const tree<T>& o) -> tree<T>& {
if(this == &o)
return *this;
node = o.node;
_children = o._children;
p = nullptr;
for(auto& c : _children)
c.set_parent(this);
return *this;
}
auto operator=(tree<T>&& o) noexcept -> tree<T>& {
if(this == &o)
return *this;
node = std::move(o.node);
_children = std::move(o._children);
p = o.p;
return *this;
}

auto operator[](size_t i) -> tree<T>& {
if(i >= _children.size())
throw std::out_of_range("tree index out of range");
return _children[i];
}

template<typename... Args>
auto emplace(Args &&... args) -> tree<T> & {
children.emplace_back(std::forward<Args...>(args)...);
auto emplace(Args &&... args) -> tree<T>& {
_children.emplace_back(std::forward<Args...>(args)...).set_parent(this);
return *this;
}
auto concat(const tree<T> &element) -> tree<T> & {
children.push_back(element);
// TODO: Naming of concat/emplace is bad
auto concat(const tree<T>& element) -> tree<T>& {
_children.push_back(element);
(_children.end()-1)->set_parent(this);
return *this;
}
auto concat(tree<T>&& e) -> tree<T>& {
_children.emplace_back(std::move(e));
(_children.end()-1)->set_parent(this);
return *this;
}
auto operator+=(const tree<T> &element) -> tree<T> & {
Expand All @@ -46,22 +169,41 @@ namespace ya {

void apply_dfs(std::function<void(T &)> f) {
f(node);
for (auto &c: children)
for (auto &c: _children)
c.apply_dfs(f);
}
void apply_dfs(std::function<void(const T &)> f) const {
f(node);
for (auto &c: children)
for (auto &c: _children)
c.apply_dfs(f);
}
void apply_dfs(std::function<void(const tree<T> &)> f) const {
f(*this);
for (auto &c: children)
for (auto &c: _children)
c.apply_dfs(f);
}
auto parent() const -> std::optional<tree<T>*> {
if(p) return {p};
return {};
}
auto children() const -> const children_t& {
return _children;
}
auto begin() -> _left_df_iterator {
return _left_df_iterator{*this};
}
auto end() -> _left_df_iterator {
return _left_df_iterator{*this, std::vector<size_t>{{_children.size()}}};
}

T node;
std::vector<tree<T>> children;
private:
children_t _children;
tree<T>* p;

void set_parent(tree<T>* new_parent) {
p = new_parent;
}
};

template<typename T>
Expand Down
19 changes: 12 additions & 7 deletions src/demo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ void print_tree_manually(const ya::tree<std::string>& tree);

int main(int argc, char** argv) {
auto printer_function = [](const std::string& node){ std::cout << node << " "; };
//// You can create trees constructor-pattern style
//// You can create trees with "emplace"
//// +
//// / \
//// 1 2
auto my_tree = ya::tree<std::string>{"+"}
.emplace("1")
.emplace("2");
.emplace("1")
.emplace("2");
my_tree.apply_dfs(printer_function); // + 1 2
std::cout << std::endl;

Expand All @@ -58,14 +58,19 @@ int main(int argc, char** argv) {
std::cout << std::endl;
my_tree2.apply_dfs(print_tree); // also (3*(1+2)) - just a lot more manual stack maintenance
std::cout << std::endl;

//// trees are even forward iterable in a DFS manner as well.
for(auto& n : my_tree2)
std::cout << n.node << " "; // * 3 + 1 2

return 0;
}

//// Prints a tree as if it was regular arithmetic ( 1 + 2 + 3 )
//// instead of reverse polish notation ( + 1 2 3 )
std::stack<std::pair<std::string, unsigned int>> counter_stack{};
void print_tree(const ya::tree<std::string>& tree) {
if(tree.children.empty()) {
if(tree. children().empty()) {
std::cout << tree.node;
while(!counter_stack.empty()) {
if(--counter_stack.top().second <= 0) {
Expand All @@ -79,18 +84,18 @@ void print_tree(const ya::tree<std::string>& tree) {
return;
}
std::cout << "(";
counter_stack.push(std::make_pair(tree.node, tree.children.size()));
counter_stack.push(std::make_pair(tree.node, tree.children().size()));
}

//// A much easier implementation of print_tree
void print_tree_manually(const ya::tree<std::string>& tree) {
if(tree.children.empty()) {
if(tree.children().empty()) {
std::cout << tree.node;
return;
}
std::cout << "(";
std::string sep{};
for(auto& c : tree.children) {
for(auto& c : tree.children()) {
std::cout << sep;
print_tree_manually(c);
sep = tree.node;
Expand Down

0 comments on commit d1f4338

Please sign in to comment.