sled/3party/cpplinq/linq.hpp

585 lines
16 KiB
C++
Raw Normal View History

2024-03-14 20:50:17 +08:00
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
///
/// namespace cpplinq
/// -----------------
///
/// Defines a number of range-based composable operators for enumerating and modifying collections
///
/// The general design philosophy is to
/// (1) emulate the composable query patterns introduced in C# Linq
/// (2) preserve iterator category and writability where possible
/// For instance, like C# Linq we have a select operator to project one sequence into a new one.
/// Unlike Linq, invoking Select on a random access sequence will yield you a _random_ access sequence.
///
/// The general workflow begins with 'from()' which normally only takes a reference
/// the the collection in question. However, from that point on, all operators function
/// by value, so that the range can store any necessary state, rather than duplicating it
/// onto iterator pairs.
///
/// In subsequent documentation, "powers" lists which powers that the operator attempts to preserve if
/// available on on the input sequence. Some iterator powers may be skipped - in such a case, round down
/// to the next supported power (e.g. if 'fwd' and 'rnd', an input of 'bidi' will round down to a 'fwd' result).
///
///
///
/// class linq_query
/// ----------------
///
/// from(container&)
/// ================
/// - Result: Query
///
/// Construct a new query, from an lvalue reference to a collection. Does not copy the collection
///
///
///
/// from(iter, iter)
/// ================
/// - Result: Query
///
/// Construct a new query, from an iterator pair.
///
///
///
/// query.select(map)
/// ==========================
/// - Result: Query
/// - Powers: input, forward, bidirectional, random access
///
/// For each element `x` in the input sequences, computes `map(x)` for the result sequence.
///
///
///
/// query.where(pred) -> query
/// ==========================
/// - Result: Query
/// - Powers: input, forward, bidirectional
///
/// Each element `x` in the input appears in the output if `pred(x)` is true.
///
/// The expression `pred(x)` is evaluated only when moving iterators (op++, op--).
/// Dereferencing (op*) does not invoke the predicate.
///
///
///
/// query.groupby(keymap [, keyequal])
/// ====================================
/// Result: Query of groups. Each group has a 'key' field, and is a query of elements from the input.
/// Powers: forward
///
///
///
/// query.any([pred])
/// =================
/// - Result: bool
///
/// (No argument) Returns true if sequence is non-empty. Equivalent to `query.begin()!=query.end()`
///
/// (One argument) Returns true if the sequence contains any elements for which `pred(element)` is true.
/// Equivalent to `query.where(pred).any()`.
///
///
///
/// query.all(pred)
/// ===============
/// - Result: bool
///
/// Returns true if `pred` holds for all elements in the sequence. Equivalent to `!query.any(std::not1(pred))`
///
///
///
/// query.take(n)
/// =============
/// - Result: query
/// - Powers: input, forward, random access (not bidirectional)
///
/// Returns a sequence that contains up to `n` items from the original sequence.
///
///
///
/// query.skip(n)
/// =============
/// - Result: query
/// - Powers: input, forward, random access (not bidirectional)
///
/// Returns a sequence that skips the first `n` items from the original sequence, or an empty sequence if
/// fewer than `n` were available on input.
///
/// Note: begin() takes O(n) time when input iteration power is weaker than random access.
///
///
///
/// query.count([pred])
/// ===================
/// - Result: std::size_t
///
/// _TODO: should use inner container's iterator distance type instead._
///
/// (Zero-argument) Returns the number of elements in the range.
/// Equivalent to `std::distance(query.begin(), query.end())`
///
/// (One-argument) Returns the number of elements for whicht `pred(element)` is true.
/// Equivalent to `query.where(pred).count()`
///
#if !defined(CPPLINQ_LINQ_HPP)
#define CPPLINQ_LINQ_HPP
#pragma once
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#include <functional>
#include <iterator>
#include <algorithm>
#include <numeric>
#include <list>
#include <map>
#include <set>
#include <memory>
#include <utility>
#include <type_traits>
#include <vector>
#include <cstddef>
// some configuration macros
#if _MSC_VER > 1600 || __cplusplus > 199711L
#define LINQ_USE_RVALUEREF 1
#endif
#if (defined(_MSC_VER) && _CPPRTTI) || !defined(_MSC_VER)
#define LINQ_USE_RTTI 1
#endif
#if defined(__clang__)
#if __has_feature(cxx_rvalue_references)
#define LINQ_USE_RVALUEREF 1
#endif
#if __has_feature(cxx_rtti)
#define LINQ_USE_RTTI 1
#endif
#endif
// individual features
#include "util.hpp"
#include "linq_cursor.hpp"
#include "linq_iterators.hpp"
#include "linq_select.hpp"
#include "linq_take.hpp"
#include "linq_skip.hpp"
#include "linq_groupby.hpp"
#include "linq_where.hpp"
#include "linq_last.hpp"
#include "linq_selectmany.hpp"
namespace cpplinq
{
namespace detail
{
template<class Pred>
struct not1_{
Pred pred;
not1_(Pred p) : pred(p)
{}
template<class T>
bool operator()(const T& value)
{
return !pred(value);
}
};
// note: VC2010's std::not1 doesn't support lambda expressions. provide our own.
template<class Pred>
not1_<Pred> not1(Pred p) { return not1_<Pred>(p); }
}
namespace detail {
template <class U>
struct cast_to {
template <class T>
U operator()(const T& value) const {
return static_cast<U>(value);
}
};
}
template <class Collection>
class linq_driver
{
typedef typename Collection::cursor::element_type
element_type;
typedef typename Collection::cursor::reference_type
reference_type;
public:
typedef cursor_iterator<typename Collection::cursor>
iterator;
linq_driver(Collection c) : c(c) {}
// -------------------- linq core methods --------------------
template <class KeyFn>
linq_driver< linq_groupby<Collection, KeyFn> > groupby(KeyFn fn)
{
return linq_groupby<Collection, KeyFn>(c, std::move(fn) );
}
// TODO: groupby(keyfn, eq)
// TODO: join...
template <class Selector>
linq_driver< linq_select<Collection, Selector> > select(Selector sel) const {
return linq_select<Collection, Selector>(c, std::move(sel) );
}
template <class Fn>
linq_driver< linq_select_many<Collection, Fn, detail::default_select_many_selector> >
select_many(Fn fn) const
{
return linq_select_many<Collection, Fn, detail::default_select_many_selector>(c, fn, detail::default_select_many_selector());
}
template <class Fn, class Fn2>
linq_driver< linq_select_many<Collection, Fn, Fn2> > select_many(Fn fn, Fn2 fn2) const
{
return linq_select_many<Collection, Fn, Fn2>(c, fn, fn2);
}
template <class Predicate>
linq_driver< linq_where<Collection, Predicate> > where(Predicate p) const {
return linq_where<Collection, Predicate>(c, std::move(p) );
}
// -------------------- linq peripheral methods --------------------
template <class Fn>
element_type aggregate(Fn fn) const
{
auto it = begin();
if (it == end()) {
return element_type();
}
reference_type first = *it;
return std::accumulate(++it, end(), first, fn);
}
template <class T, class Fn>
T aggregate(T initialValue, Fn fn) const
{
return std::accumulate(begin(), end(), initialValue, fn);
}
bool any() const { auto cur = c.get_cursor(); return !cur.empty(); }
template <class Predicate>
bool any(Predicate p) const {
auto it = std::find_if(begin(), end(), p);
return it != end();
}
template <class Predicate>
bool all(Predicate p) const {
auto it = std::find_if(begin(), end(), detail::not1(p));
return it == end();
}
// TODO: average
#if !defined(__clang__)
// Clang complains that linq_driver is not complete until the closing brace
// so (linq_driver*)->select() cannot be resolved.
template <class U>
auto cast()
-> decltype(static_cast<linq_driver*>(0)->select(detail::cast_to<U>()))
{
return this->select(detail::cast_to<U>());
}
#endif
// TODO: concat
bool contains(const typename Collection::cursor::element_type& value) const {
return std::find(begin(), end(), value) != end();
}
typename std::iterator_traits<iterator>::difference_type count() const {
return std::distance(begin(), end());
}
template <class Predicate>
typename std::iterator_traits<iterator>::difference_type count(Predicate p) const {
auto filtered = this->where(p);
return std::distance(begin(filtered), end(filtered));
}
// TODO: default_if_empty
// TODO: distinct()
// TODO: distinct(cmp)
reference_type element_at(std::size_t ix) const {
auto cur = c.get_cursor();
while(ix && !cur.empty()) {
cur.inc();
--ix;
}
if (cur.empty()) { throw std::logic_error("index out of bounds"); }
else { return cur.get(); }
}
element_type element_at_or_default(std::size_t ix) const {
auto cur = c.get_cursor();
while(ix && !cur.empty()) {
cur.inc();
-- ix;
}
if (cur.empty()) { return element_type(); }
else { return cur.get(); }
}
bool empty() const {
return !this->any();
}
// TODO: except(second)
// TODO: except(second, eq)
reference_type first() const {
auto cur = c.get_cursor();
if (cur.empty()) { throw std::logic_error("index out of bounds"); }
else { return cur.get(); }
}
template <class Predicate>
reference_type first(Predicate pred) const {
auto cur = c.get_cursor();
while (!cur.empty() && !pred(cur.get())) {
cur.inc();
}
if (cur.empty()) { throw std::logic_error("index out of bounds"); }
else { return cur.get(); }
}
element_type first_or_default() const {
auto cur = c.get_cursor();
if (cur.empty()) { return element_type(); }
else { return cur.get(); }
}
template <class Predicate>
element_type first_or_default(Predicate pred) const {
auto cur = c.get_cursor();
while (!cur.empty() && !pred(cur.get())) {
cur.inc();
}
if (cur.empty()) { return element_type(); }
else { return cur.get(); }
}
// TODO: intersect(second)
// TODO: intersect(second, eq)
// note: forward cursors and beyond can provide a clone, so we can refer to the element directly
typename std::conditional<
std::is_convertible<
typename Collection::cursor::cursor_category,
forward_cursor_tag>::value,
reference_type,
element_type>::type
last() const
{
return linq_last_(c.get_cursor(), typename Collection::cursor::cursor_category());
}
template <class Predicate>
reference_type last(Predicate pred) const
{
auto cur = c.where(pred).get_cursor();
return linq_last_(cur, typename decltype(cur)::cursor_category());
}
element_type last_or_default() const
{
return linq_last_or_default_(c.get_cursor(), typename Collection::cursor::cursor_category());
}
template <class Predicate>
element_type last_or_default(Predicate pred) const
{
auto cur = c.where(pred).get_cursor();
return linq_last_or_default_(cur, typename decltype(cur)::cursor_category());
}
reference_type max() const
{
return max(std::less<element_type>());
}
template <class Compare>
reference_type max(Compare less) const
{
auto it = std::max_element(begin(), end(), less);
if (it == end())
throw std::logic_error("max performed on empty range");
return *it;
}
reference_type min() const
{
return min(std::less<element_type>());
}
template <class Compare>
reference_type min(Compare less) const
{
auto it = std::min_element(begin(), end(), less);
if (it == end())
throw std::logic_error("max performed on empty range");
return *it;
}
// TODO: order_by(sel)
// TODO: order_by(sel, less)
// TODO: order_by_descending(sel)
// TODO: order_by_descending(sel, less)
// TODO: sequence_equal(second)
// TODO: sequence_equal(second, eq)
// TODO: single / single_or_default
linq_driver<linq_skip<Collection>> skip(std::size_t n) const {
return linq_skip<Collection>(c, n);
}
// TODO: skip_while(pred)
template<typename ITEM = element_type>
typename std::enable_if<std::is_default_constructible<ITEM>::value, ITEM>::type sum() const {
ITEM seed{};
return sum(seed);
}
element_type sum(element_type seed) const {
return std::accumulate(begin(), end(), seed);
}
template <typename Selector, typename Result = typename std::result_of<Selector(element_type)>::type>
typename std::enable_if<std::is_default_constructible<Result>::value, Result>::type sum(Selector sel) const {
return from(begin(), end()).select(sel).sum();
}
template <typename Selector, typename Result = typename std::result_of<Selector(element_type)>::type>
Result sum(Selector sel, Result seed) const {
return from(begin(), end()).select(sel).sum(seed);
}
linq_driver<linq_take<Collection>> take(std::size_t n) const {
return linq_take<Collection>(c, n);
}
// TODO: take_while
// TODO: then_by / then_by_descending ?
// TODO: to_...
// TODO: union(second)
// TODO: union(eq)
// TODO: zip
// -------------------- conversion methods --------------------
std::vector<typename Collection::cursor::element_type> to_vector() const
{
return std::vector<typename Collection::cursor::element_type>(begin(), end());
}
std::list<typename Collection::cursor::element_type> to_list() const
{
return std::list<typename Collection::cursor::element_type>(begin(), end());
}
std::set<typename Collection::cursor::element_type> to_set() const
{
return std::set<typename Collection::cursor::element_type>(begin(), end());
}
// -------------------- container/range methods --------------------
iterator begin() const { auto cur = c.get_cursor(); return !cur.empty() ? iterator(cur) : iterator(); }
iterator end() const { return iterator(); }
linq_driver& operator=(const linq_driver& other) { c = other.c; return *this; }
template <class TC2>
linq_driver& operator=(const linq_driver<TC2>& other) { c = other.c; return *this; }
typename std::iterator_traits<iterator>::reference
operator[](std::size_t ix) const {
return *(begin()+=ix);
}
// -------------------- collection methods (leaky abstraction) --------------------
typedef typename Collection::cursor cursor;
cursor get_cursor() { return c.get_cursor(); }
linq_driver< dynamic_collection<typename Collection::cursor::reference_type> >
late_bind() const
{
return dynamic_collection<typename Collection::cursor::reference_type>(c);
}
private:
Collection c;
};
// TODO: should probably use reference-wrapper instead?
template <class TContainer>
linq_driver<iter_cursor<typename util::container_traits<TContainer>::iterator>> from(TContainer& c)
{
auto cur = iter_cursor<typename util::container_traits<TContainer>::iterator>(std::begin(c), std::end(c));
return cur;
}
template <class T>
const linq_driver<T>& from(const linq_driver<T>& c)
{
return c;
}
template <class Iter>
linq_driver<iter_cursor<Iter>> from(Iter start, Iter finish)
{
return iter_cursor<Iter>(start, finish);
}
template <class TContainer>
linq_driver<TContainer> from_value(const TContainer& c)
{
return linq_driver<TContainer>(c);
}
}
#pragma pop_macro("min")
#pragma pop_macro("max")
#endif // defined(CPPLINQ_LINQ_HPP)