-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathimmutable-list.h
131 lines (103 loc) · 3.64 KB
/
immutable-list.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#ifndef AGENCY_IMMUTABLE_LIST
#define AGENCY_IMMUTABLE_LIST
template<typename T>
struct immutable_list {
immutable_list() noexcept = default;
immutable_list(immutable_list const&) = default;
immutable_list(immutable_list&&) noexcept = default;
immutable_list(tail_container const& t) : _head(t._tail_start) {}
immutable_list& operator=(immutable_list const&) = default;
immutable_list& operator=(immutable_list&&) noexcept = default;
explicit immutable_list(std::initializer_list<T> list) noexcept {
container_pointer current;
for (auto &element : list) {
container_pointer next = std::make_shared<element_container>(nullptr, element);
if (head == nullptr) {
head = next;
} else {
current->next = next;
}
current = next;
}
};
immutable_list<T> push_front(T t) const {
return immutable_list(std::make_shared<element_container>(head, std::move(t)));
}
template<typename... As>
immutable_list<T> emplace_front(As&&... a) const {
return immutable_list(std::make_shared<element_container>(head, std::forward<As>(a)...));
}
operator bool() const noexcept { return head.operator bool(); }
bool empty() const noexcept { return head; }
immutable_list<T> const tail() const noexcept { return immutable_list(head ? head->next : nullptr); }
tail_container tail() noexcept { return tail_container(head ? head->next : nullptr); }
T const& head() const noexcept { return head->value; }
T& head() noexcept { return head->value; }
bool operator==(immutable_list<T> const& other) const noexcept {
if (head == nullptr || other.head == nullptr) {
return head == other.head;
}
if (head->value == other.head->value) {
return tail() == other.tail();
}
return false;
}
bool has_prefix(immutable_list<T> const& prefix) const noexcept {
if (prefix.head == nullptr) {
return true;
}
if (head == nullptr) {
return false;
}
if (head->value == prefix.head->value) {
return tail().has_prefix(prefix.tail());
}
return false;
}
bool operator<(immutable_list<T> const& other) const noexcept {
if (other.head == nullptr) {
return false;
}
if (head == nullptr) {
return true;
}
if (head->value < other.head->value) {
return tail() < other.tail();
}
return false;
}
template<int I>
std::conditional_t<I == 0, T const&, immutable_list<T>> get() const noexcept {
static_assert(0 <= I && I <= 1);
if constexpr (I == 0) {
return head();
} else if constexpr (I == 1) {
return tail();
}
}
public:
class tail_container {
container_pointer const& _tail_start;
tail_container(container_pointer const& tail) : _tail_start(tail) {}
friend immutable_list;
};
private:
struct element_container;
using container_pointer = std::shared_ptr<element_container>;
struct element_container {
container_pointer next;
T value;
template<typename... As>
explicit element_container(container_pointer next, As&&... a) : next(std::move(next)), value(std::forward<As>(a)...) {}
explicit element_container(container_pointer next, T value) : next(std::move(next)), value(std::move(value)) {}
};
explicit immutable_list(container_pointer head) noexcept : head(std::move(head)) {}
container_pointer head;
};
template<typename T>
struct std::tuple_size<immutable_list<T>> : std::integral_constant<std::size_t, 2> {};
template<typename T>
struct std::tuple_element<0, immutable_list<T>> { using type = T; };
template<typename T>
struct std::tuple_element<1, immutable_list<T>> { using type = immutable_list<T>; };
#endif // AGENCY_IMMUTABLE_LIST