File size: 5,138 Bytes
158b61b |
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 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
#pragma once
#include <queue>
#include <vector>
namespace Moses
{
namespace Syntax
{
// A container that can hold up to k objects of type T, each with an associated
// priority. The container accepts new elements unconditionally until the
// limit is reached. After that, elements are only accepted if they have a
// higher priority than the worst element (which they displace).
//
// BoundedPriorityContainer does not preserve the insertion order of the
// elements (or provide any other guarantees about order).
//
// BoundedPriorityContainer pre-allocates space for all k objects.
//
// (Although BoundedPriorityContainer is implemented using a priority queue,
// it doesn't provide the interface of a priority queue, hence the generic
// name 'container'.)
template<typename T>
class BoundedPriorityContainer
{
public:
typedef typename std::vector<T>::iterator Iterator;
typedef typename std::vector<T>::const_iterator ConstIterator;
BoundedPriorityContainer(std::size_t);
Iterator Begin() {
return m_elements.begin();
}
Iterator End() {
return m_elements.begin()+m_size;
}
ConstIterator Begin() const {
return m_elements.begin();
}
ConstIterator End() const {
return m_elements.begin()+m_size;
}
// Return the number of elements currently held.
std::size_t Size() const {
return m_size;
}
// 'Lazily' clear the container by setting the size to 0 (allowing elements
// to be overwritten).
// TODO Eliminate heap-reorganisation overhead by using a vector-based heap
// TODO directly instead of priority_queue, which requires pop() to clear
// TODO Alternative, is to clear m_queue by assigning an empty queue value
// TODO but that might incur an alloc-related overhead when the new underlying
// TODO has to be regrown.
void LazyClear() {
m_size = 0;
while (!m_queue.empty()) {
m_queue.pop();
}
}
// Insert the given object iff
// i) the container is not full yet, or
// ii) the new object has a higher priority than the worst one already
// stored.
// The return value specifies whether or not the element was inserted.
bool Insert(const T &, float);
// Insert the given object iff
// i) the container is not full yet, or
// ii) the new object has a higher priority than the worst one already
// stored.
// If the element is inserted then, for efficiency reasons, it is swapped in
// rather than copied. This requires that T provides a swap() function. The
// return value specifies whether or not the element was inserted.
// TODO Test if this is actually any faster than Insert() in practice.
bool SwapIn(T &, float);
// Determine if an object with the given priority would be accepted for
// insertion based on the current contents of the container.
bool WouldAccept(float priority) {
return m_size < m_limit || priority > m_queue.top().first;
}
private:
typedef std::pair<float, int> PriorityIndexPair;
class PriorityIndexPairOrderer
{
public:
bool operator()(const PriorityIndexPair &p,
const PriorityIndexPair &q) const {
return p.first > q.first;
}
};
// Min-priority queue. The queue stores the indices of the elements, not
// the elements themselves to keep down the costs of heap maintenance.
typedef std::priority_queue<PriorityIndexPair,
std::vector<PriorityIndexPair>,
PriorityIndexPairOrderer> Queue;
// The elements are stored in a vector. Note that the size of this vector
// can be greater than m_size (after a call to LazyClear).
std::vector<T> m_elements;
// The number of elements currently held.
std::size_t m_size;
// The maximum number of elements.
const std::size_t m_limit;
// The min-priority queue.
Queue m_queue;
};
template<typename T>
BoundedPriorityContainer<T>::BoundedPriorityContainer(std::size_t limit)
: m_size(0)
, m_limit(limit)
{
m_elements.reserve(m_limit);
}
template<typename T>
bool BoundedPriorityContainer<T>::Insert(const T &t, float priority)
{
if (m_size < m_limit) {
PriorityIndexPair pair(priority, m_size);
m_queue.push(pair);
if (m_size < m_elements.size()) {
m_elements[m_size] = t;
} else {
m_elements.push_back(t);
}
++m_size;
return true;
} else if (priority > m_queue.top().first) {
PriorityIndexPair pair = m_queue.top();
m_queue.pop();
pair.first = priority;
m_elements[pair.second] = t;
m_queue.push(pair);
return true;
}
return false;
}
template<typename T>
bool BoundedPriorityContainer<T>::SwapIn(T &t, float priority)
{
if (m_size < m_limit) {
PriorityIndexPair pair(priority, m_size);
m_queue.push(pair);
if (m_size < m_elements.size()) {
swap(m_elements[m_size], t);
} else {
m_elements.push_back(t);
}
++m_size;
return true;
} else if (priority > m_queue.top().first) {
PriorityIndexPair pair = m_queue.top();
m_queue.pop();
pair.first = priority;
swap(m_elements[pair.second], t);
m_queue.push(pair);
return true;
}
return false;
}
} // Syntax
} // Moses
|