-
-
Save Dobiasd/8a1cf9d52532495ebf03d1ef2d876ddd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. | |
// https://github.com/Dobiasd/FunctionalPlus | |
// Distributed under the Boost Software License, Version 1.0. | |
// (See accompanying file LICENSE_1_0.txt or copy at | |
// http://www.boost.org/LICENSE_1_0.txt) | |
#pragma once | |
#include <fplus/compare.hpp> | |
#include <fplus/composition.hpp> | |
#include <fplus/container_traits.hpp> | |
#include <fplus/maybe.hpp> | |
#include <fplus/internal/container_common.hpp> | |
#include <fplus/internal/invoke.hpp> | |
#include <fplus/internal/meta.hpp> | |
#include <algorithm> | |
#include <cassert> | |
#include <cmath> | |
#include <cstddef> | |
#include <iterator> | |
#include <numeric> | |
namespace fplus { | |
namespace internal { | |
template <typename UnaryPredicate, typename Container> | |
void check_unary_predicate_for_container() { | |
internal::check_unary_predicate_for_type<UnaryPredicate, | |
typename Container::value_type>(); | |
} | |
template <typename F, typename Container> | |
void check_index_with_type_predicate_for_container() { | |
typedef typename Container::value_type T; | |
internal::trigger_static_asserts<internal::binary_function_tag, F, | |
std::size_t, T>(); | |
static_assert( | |
std::is_convertible<internal::invoke_result_t<F, std::size_t, T>, | |
bool>::value, | |
"Function must return bool."); | |
} | |
template <typename Compare, typename Container> | |
void check_compare_for_container() { | |
typedef typename Container::value_type T; | |
internal::trigger_static_asserts<internal::binary_predicate_tag, Compare, T, | |
T>(); | |
} | |
template <typename BinaryPredicate, typename Container> | |
void check_binary_predicate_for_container() { | |
typedef typename Container::value_type T; | |
internal::trigger_static_asserts<internal::binary_predicate_tag, | |
BinaryPredicate, T, T>(); | |
} | |
// PrepareContainer and BackInserter are overloaded | |
// to increase performance on std::vector and std::string | |
// by using std::vector<T>::reserve | |
// and std::back_inserter instead of std::back_inserter. | |
// In VC2015, release mode, Celsius W520 Xeon | |
// this leads to an increase in performance of about a factor of 3 | |
// for transform. | |
template <typename C> | |
void prepare_container( | |
const std::basic_string<C, std::char_traits<C>, std::allocator<C>>& ys, | |
std::size_t size) { | |
ys.reserve(size); | |
} | |
template <typename Y> | |
void prepare_container(std::vector<Y>& ys, std::size_t size) { | |
ys.reserve(size); | |
} | |
template <typename T, std::size_t N> | |
void prepare_container(std::array<T, N>&, std::size_t size) { | |
assert(size == N); | |
unused(size); | |
} | |
template <typename Y> | |
void prepare_container(std::unordered_set<Y>& ys, std::size_t size) { | |
ys.reserve(size); | |
} | |
template <typename Key, typename T> | |
void prepare_container(std::unordered_map<Key, T>& ys, std::size_t size) { | |
ys.reserve(size); | |
} | |
template <typename Y> | |
void prepare_container(std::unordered_multiset<Y>& ys, std::size_t size) { | |
ys.reserve(size); | |
} | |
template <typename Key, typename T> | |
void prepare_container(std::unordered_multimap<Key, T>& ys, std::size_t size) { | |
ys.reserve(size); | |
} | |
template <typename Container> | |
void prepare_container(Container&, std::size_t) {} | |
template <typename Container> | |
std::back_insert_iterator<Container> get_back_inserter(std::string& ys) { | |
return std::back_inserter(ys); | |
} | |
template <typename Container, typename Y> | |
std::back_insert_iterator<Container> get_back_inserter(std::vector<Y>& ys) { | |
return std::back_inserter(ys); | |
} | |
template <typename Container, typename Y> | |
std::back_insert_iterator<Container> get_back_inserter(std::list<Y>& ys) { | |
return std::back_inserter(ys); | |
} | |
template <typename Container, typename Y> | |
std::back_insert_iterator<Container> get_back_inserter(std::deque<Y>& ys) { | |
return std::back_inserter(ys); | |
} | |
// Avoid self-assignment. | |
template <typename T> | |
void assign(T& x, T&& y) { | |
if (&x != &y) | |
x = std::move(y); | |
} | |
template <typename T, std::size_t N> | |
struct array_back_insert_iterator | |
: public std::back_insert_iterator<std::array<T, N>> { | |
typedef std::back_insert_iterator<std::array<T, N>> base_type; | |
explicit array_back_insert_iterator(std::array<T, N>& arr) | |
: base_type(arr), arr_ptr_(&arr), pos_(0) {} | |
array_back_insert_iterator(const array_back_insert_iterator<T, N>& other) | |
: base_type(*other.arr_ptr_), | |
arr_ptr_(other.arr_ptr_), | |
pos_(other.pos_) {} | |
array_back_insert_iterator<T, N>& operator=( | |
const array_back_insert_iterator<T, N>& other) { | |
arr_ptr_ = other.arr_ptr_; | |
pos_ = other.pos_; | |
return *this; | |
} | |
~array_back_insert_iterator() { assert(pos_ == 0 || pos_ == N); } | |
array_back_insert_iterator<T, N>& operator=(const T& x) { | |
assert(pos_ < N); | |
(*arr_ptr_)[pos_] = x; | |
++pos_; | |
return *this; | |
} | |
array_back_insert_iterator<T, N>& operator=(T&& x) { | |
assert(pos_ < N); | |
assign((*arr_ptr_)[pos_], std::move(x)); | |
++pos_; | |
return *this; | |
} | |
array_back_insert_iterator<T, N>& operator*() { return *this; } | |
array_back_insert_iterator<T, N>& operator++() { return *this; } | |
array_back_insert_iterator<T, N> operator++(int) { return *this; } | |
private: | |
std::array<T, N>* arr_ptr_; | |
std::size_t pos_; | |
}; | |
#if defined(_MSC_VER) && _MSC_VER >= 1900 | |
template <typename T, std::size_t N> | |
struct std::_Is_checked_helper<array_back_insert_iterator<T, N>> | |
: public true_type { // mark array_back_insert_iterator as checked | |
}; | |
#endif | |
template <typename Container, typename Y, std::size_t N> | |
array_back_insert_iterator<Y, N> get_back_inserter(std::array<Y, N>& ys) { | |
return array_back_insert_iterator<Y, N>(ys); | |
} | |
template <typename Container> | |
std::insert_iterator<Container> get_back_inserter(Container& ys) { | |
return std::inserter(ys, std::end(ys)); | |
} | |
template <typename Iterator> | |
void advance_iterator(Iterator& it, std::size_t distance) { | |
std::advance(it, static_cast<typename Iterator::difference_type>(distance)); | |
} | |
template <typename T> | |
void advance_iterator(T*& it, std::size_t distance) { | |
it += static_cast<std::ptrdiff_t>(distance); | |
} | |
template <typename Iterator> | |
Iterator add_to_iterator(Iterator it, std::size_t distance = 1) { | |
return std::next(it, | |
static_cast<typename Iterator::difference_type>(distance)); | |
} | |
// GCC 4.9 does not support std::rbegin, std::rend and | |
// std::make_reverse_iterator | |
template <typename Iterator> | |
std::reverse_iterator<Iterator> make_reverse_iterator(Iterator it) { | |
return std::reverse_iterator<Iterator>(it); | |
} | |
} // namespace internal | |
// API search type: is_even : Int -> Bool | |
// fwd bind count: 0 | |
// Checks if x is even. | |
template <typename X> | |
bool is_even(X x) { | |
static_assert(std::is_integral<X>::value, "type must be integral"); | |
return x % 2 == 0; | |
} | |
// API search type: is_odd : Int -> Bool | |
// fwd bind count: 0 | |
// Checks if x is odd. | |
template <typename X> | |
bool is_odd(X x) { | |
static_assert(std::is_integral<X>::value, "type must be integral"); | |
return x % 2 != 0; | |
} | |
// API search type: is_empty : [a] -> Bool | |
// fwd bind count: 0 | |
// Returns true if the container holds no elements. | |
// is_empty([1, 2]) == false | |
// is_empty([]) == true | |
template <typename Container> | |
bool is_empty(const Container& xs) { | |
return xs.empty(); | |
} | |
// API search type: is_not_empty : [a] -> Bool | |
// fwd bind count: 0 | |
// Returns true if the container holds at least one element. | |
// is_not_empty([1, 2]) == true | |
template <typename Container> | |
bool is_not_empty(const Container& xs) { | |
return !is_empty(xs); | |
} | |
// API search type: size_of_cont : [a] -> Int | |
// fwd bind count: 0 | |
// Returns the number of elements in the given container. | |
// size_of_cont([3, 4]) == 2 | |
template <typename Container> | |
std::size_t size_of_cont(const Container& xs) { | |
return xs.size(); | |
} | |
// API search type: convert : a -> b | |
// fwd bind count: 0 | |
// Converts one type of element into another. | |
template <typename Dest, typename Source> | |
Dest convert(const Source& x) { | |
return Dest(x); | |
} | |
// API search type: convert_elems : [a] -> [b] | |
// fwd bind count: 0 | |
// Converts all elements in a sequence to a different type. | |
// convert_elems<NewT>([1, 2, 3]) == [NewT(1), NewT(2), NewT(3)] | |
template <typename NewT, | |
typename ContainerIn, | |
typename ContainerOut = | |
typename internal::same_cont_new_t<ContainerIn, NewT, 0>::type> | |
ContainerOut convert_elems(const ContainerIn& xs) { | |
static_assert( | |
std::is_constructible<NewT, typename ContainerIn::value_type>::value, | |
"Elements not convertible."); | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it = internal::get_back_inserter<ContainerOut>(ys); | |
// using 'for (const auto& x ...)' is even for ints as fast as | |
// using 'for (int x ...)' (GCC, O3), so there is no need to | |
// check if the type is fundamental and then dispatch accordingly. | |
for (const auto& x : xs) { | |
*it = convert<NewT>(x); | |
} | |
return ys; | |
} | |
// API search type: convert_container : [a] -> [a] | |
// fwd bind count: 0 | |
// Change the type of the container | |
// while keeping the elements in the sequence the same. | |
// convert_container([1, 2, 3]) == [1, 2, 3] | |
// Useful for example if you want to convert an std::list to an std::vector. | |
template <typename ContainerOut, typename ContainerIn> | |
ContainerOut convert_container(const ContainerIn& xs) { | |
typedef typename ContainerIn::value_type SourceElem; | |
typedef typename ContainerOut::value_type DestElem; | |
static_assert(std::is_same<DestElem, SourceElem>::value, | |
"Source and dest container must have the same value_type"); | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto itOut = internal::get_back_inserter<ContainerOut>(ys); | |
std::copy(std::begin(xs), std::end(xs), itOut); | |
return ys; | |
} | |
// API search type: convert_container_and_elems : [a] -> [b] | |
// fwd bind count: 0 | |
// Converts between different containers and elements. | |
// Dest elements are allowed to have explicit constructors. | |
// convert([1, 2, 3]) == [1, 2, 3] | |
template <typename ContainerOut, typename ContainerIn> | |
ContainerOut convert_container_and_elems(const ContainerIn& xs) { | |
static_assert(std::is_convertible<typename ContainerIn::value_type, | |
typename ContainerOut::value_type>::value, | |
"Elements not convertible."); | |
typedef typename ContainerOut::value_type DestElem; | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it = internal::get_back_inserter<ContainerOut>(ys); | |
for (const auto& x : xs) { | |
*it = convert<DestElem>(x); | |
} | |
return ys; | |
} | |
namespace internal { | |
template <typename Container> | |
Container get_segment(internal::reuse_container_t, | |
std::size_t idx_begin, | |
std::size_t idx_end, | |
Container&& xs) { | |
idx_end = std::min(idx_end, size_of_cont(xs)); | |
if (idx_end <= idx_begin) { | |
xs.clear(); | |
return std::forward<Container>(xs); | |
} | |
auto itBegin = std::begin(xs); | |
internal::advance_iterator(itBegin, idx_begin); | |
auto itEnd = itBegin; | |
internal::advance_iterator(itEnd, idx_end - idx_begin); | |
xs.erase(std::copy(itBegin, itEnd, std::begin(xs)), std::end(xs)); | |
return std::forward<Container>(xs); | |
} | |
template <typename Container> | |
Container get_segment(internal::create_new_container_t, | |
std::size_t idx_begin, | |
std::size_t idx_end, | |
const Container& xs) { | |
idx_end = std::min(idx_end, size_of_cont(xs)); | |
if (idx_end <= idx_begin) { | |
return {}; | |
} | |
Container result; | |
auto itBegin = std::begin(xs); | |
internal::advance_iterator(itBegin, idx_begin); | |
auto itEnd = itBegin; | |
internal::advance_iterator(itEnd, idx_end - idx_begin); | |
std::copy(itBegin, itEnd, internal::get_back_inserter(result)); | |
return result; | |
} | |
} // namespace internal | |
// API search type: get_segment : (Int, Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Return a defined segment from the sequence. | |
// get_segment(2, 5, [0,1,2,3,4,5,6,7,8]) == [2,3,4] | |
// get_segment(2, 15, [0,1,2,3,4,5,6,7,8]) == [2,3,4,5,6,7,8] | |
// get_segment(5, 2, [0,1,2,3,4,5,6,7,8]) == [] | |
// Also known as slice. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut get_segment(std::size_t idx_begin, | |
std::size_t idx_end, | |
Container&& xs) { | |
return internal::get_segment(internal::can_reuse_v<Container>{}, idx_begin, | |
idx_end, std::forward<Container>(xs)); | |
} | |
namespace internal { | |
template <typename ContainerToken, typename Container> | |
Container set_segment(internal::reuse_container_t, | |
std::size_t idx_begin, | |
const ContainerToken& token, | |
Container&& xs) { | |
assert(idx_begin + size_of_cont(token) < size_of_cont(xs)); | |
auto itBegin = std::begin(xs); | |
internal::advance_iterator(itBegin, idx_begin); | |
std::copy(std::begin(token), std::end(token), itBegin); | |
return std::forward<Container>(xs); | |
} | |
template <typename ContainerToken, typename Container> | |
Container set_segment(internal::create_new_container_t, | |
std::size_t idx_begin, | |
const ContainerToken& token, | |
const Container& xs) { | |
Container result = xs; | |
return set_segment(internal::reuse_container_t(), idx_begin, token, | |
std::move(result)); | |
} | |
} // namespace internal | |
// API search type: set_segment : (Int, [a], [a]) -> [a] | |
// fwd bind count: 2 | |
// Replace part of a sequence with a token. | |
// set_segment(2, [9,9,9], [0,1,2,3,4,5,6,7,8]) == [0,1,9,9,9,5,6,7,8] | |
// Crashes on invalid indices. | |
// Also known as replace_segment. | |
template <typename ContainerToken, | |
typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut set_segment(std::size_t idx_begin, | |
const ContainerToken& token, | |
Container&& xs) { | |
return internal::set_segment(internal::can_reuse_v<Container>{}, idx_begin, | |
token, std::forward<Container>(xs)); | |
} | |
namespace internal { | |
template <typename Container> | |
Container remove_segment(internal::reuse_container_t, | |
std::size_t idx_begin, | |
std::size_t idx_end, | |
Container&& xs) { | |
assert(idx_begin <= idx_end); | |
assert(idx_end <= size_of_cont(xs)); | |
auto firstBreakIt = std::begin(xs); | |
internal::advance_iterator(firstBreakIt, idx_begin); | |
auto secondBreakIt = std::begin(xs); | |
internal::advance_iterator(secondBreakIt, idx_end); | |
xs.erase(std::copy(secondBreakIt, std::end(xs), firstBreakIt), std::end(xs)); | |
return std::forward<Container>(xs); | |
} | |
template <typename Container> | |
Container remove_segment(internal::create_new_container_t, | |
std::size_t idx_begin, | |
std::size_t idx_end, | |
const Container& xs) { | |
assert(idx_begin <= idx_end); | |
assert(idx_end <= size_of_cont(xs)); | |
Container result; | |
std::size_t length = idx_end - idx_begin; | |
internal::prepare_container(result, size_of_cont(xs) - length); | |
auto firstBreakIt = std::begin(xs); | |
internal::advance_iterator(firstBreakIt, idx_begin); | |
std::copy(std::begin(xs), firstBreakIt, internal::get_back_inserter(result)); | |
auto secondBreakIt = std::begin(xs); | |
internal::advance_iterator(secondBreakIt, idx_end); | |
std::copy(secondBreakIt, std::end(xs), internal::get_back_inserter(result)); | |
return result; | |
} | |
} // namespace internal | |
// API search type: remove_segment : (Int, Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Cuts our a defined segment from the sequence. | |
// remove_segment(2, 5, [0,1,2,3,4,5,6,7]) == [0,1,5,6,7] | |
// crashes on invalid indices | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut remove_segment(std::size_t idx_begin, | |
std::size_t idx_end, | |
Container&& xs) { | |
return internal::remove_segment(internal::can_reuse_v<Container>{}, idx_begin, | |
idx_end, std::forward<Container>(xs)); | |
} | |
// API search type: insert_at : (Int, [a], [a]) -> [a] | |
// fwd bind count: 2 | |
// Inserts a token into a sequence at a specific position. | |
// insert_at(2, [8,9], [0,1,2,3,4]) == [0,1,8,9,2,3,4] | |
// Unsafe! Crashes on invalid index. | |
template <typename Container> | |
Container insert_at(std::size_t idx_begin, | |
const Container& token, | |
const Container& xs) { | |
assert(idx_begin <= size_of_cont(xs)); | |
Container result; | |
internal::prepare_container(result, size_of_cont(xs) + size_of_cont(token)); | |
auto breakIt = std::begin(xs); | |
internal::advance_iterator(breakIt, idx_begin); | |
std::copy(std::begin(xs), breakIt, internal::get_back_inserter(result)); | |
std::copy(std::begin(token), std::end(token), | |
internal::get_back_inserter(result)); | |
std::copy(breakIt, std::end(xs), internal::get_back_inserter(result)); | |
return result; | |
} | |
// API search type: elem_at_idx : (Int, [a]) -> a | |
// fwd bind count: 1 | |
// Return the nth element of a sequence. | |
// elem_at_idx(2, [7,6,5,4,3]) == 5 | |
// Unsafe! Crashes on invalid index. | |
template <typename Container> | |
auto elem_at_idx(std::size_t idx, const Container& xs) { | |
assert(idx < size_of_cont(xs)); | |
auto it = std::begin(xs); | |
internal::advance_iterator(it, idx); | |
return *it; | |
} | |
// API search type: elem_at_idx_maybe : (Int, [a]) -> Maybe a | |
// fwd bind count: 1 | |
// Return the nth element of a sequence if existing. | |
// elem_at_idx_maybe(2, [7,6,5,4,3]) == Just 5 | |
// elem_at_idx_maybe(9, [7,6,5,4,3]) == Nothing | |
// Use elem_at_idx_or_nothing if you want to provide a signed index type. | |
template <typename Container, typename T = typename Container::value_type> | |
maybe<T> elem_at_idx_maybe(std::size_t idx, const Container& xs) { | |
if (size_of_cont(xs) < idx) { | |
return {}; | |
} | |
auto it = std::begin(xs); | |
internal::advance_iterator(it, idx); | |
return *it; | |
} | |
// API search type: elems_at_idxs : ([Int], [a]) -> [a] | |
// fwd bind count: 1 | |
// Construct a subsequence from the elements with the given indices. | |
// elem_at_idxs([1, 3], [7,6,5,4,3]) == [6, 4] | |
template <typename Container, | |
typename ContainerIdxs, | |
typename T = typename Container::value_type, | |
typename ContainerOut = std::vector<T>> | |
std::vector<T> elems_at_idxs(const ContainerIdxs& idxs, const Container& xs) { | |
static_assert( | |
std::is_same<typename ContainerIdxs::value_type, std::size_t>::value, | |
"Indices must be std::size_t"); | |
ContainerOut result; | |
internal::prepare_container(result, size_of_cont(idxs)); | |
auto itOut = internal::get_back_inserter(result); | |
for (std::size_t idx : idxs) { | |
*itOut = elem_at_idx(idx, xs); | |
} | |
return result; | |
} | |
namespace internal { | |
template <typename Container, typename F> | |
Container transform(internal::reuse_container_t, F f, Container&& xs) { | |
internal::trigger_static_asserts<internal::unary_function_tag, F, | |
decltype(*std::begin(xs))>(); | |
std::transform(std::begin(xs), std::end(xs), std::begin(xs), f); | |
return std::forward<Container>(xs); | |
} | |
template <typename ContainerOut, typename F, typename ContainerIn> | |
ContainerOut transform(internal::create_new_container_t, | |
F f, | |
const ContainerIn& xs) { | |
internal::trigger_static_asserts<internal::unary_function_tag, F, | |
decltype(*std::begin(xs))>(); | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it = internal::get_back_inserter<ContainerOut>(ys); | |
std::transform(std::begin(xs), std::end(xs), it, f); | |
return ys; | |
} | |
} // namespace internal | |
// API search type: transform : ((a -> b), [a]) -> [b] | |
// fwd bind count: 1 | |
// Apply a function to every element in a sequence. | |
// transform((*2), [1, 3, 4]) == [2, 6, 8] | |
// Also known as map or fmap. | |
template < | |
typename F, | |
typename ContainerIn, | |
typename ContainerOut = typename internal::same_cont_new_t_from_unary_f< | |
internal::remove_const_and_ref_t<ContainerIn>, | |
F, | |
0>::type> | |
ContainerOut transform(F f, ContainerIn&& xs) { | |
using reuse_t = typename std::conditional< | |
std::is_same<internal::can_reuse_v<ContainerIn>, | |
internal::reuse_container_t>::value && | |
std::is_base_of<std::true_type, | |
internal::has_order<ContainerIn>>::value && | |
std::is_same<internal::remove_const_and_ref_t<ContainerIn>, | |
ContainerOut>::value, | |
internal::reuse_container_t, internal::create_new_container_t>::type; | |
return internal::transform<ContainerOut>(reuse_t{}, f, | |
std::forward<ContainerIn>(xs)); | |
} | |
// API search type: transform_convert : ((a -> b), [a]) -> [b] | |
// fwd bind count: 1 | |
// transform_convert((*2), [1, 3, 4]) == [2, 6, 8] | |
// Same as transform, but makes it easy to | |
// use an output container type different from the input container type. | |
template <typename ContainerOut, typename F, typename ContainerIn> | |
ContainerOut transform_convert(F f, const ContainerIn& xs) { | |
internal::trigger_static_asserts<internal::unary_function_tag, F, | |
typename ContainerIn::value_type>(); | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it = internal::get_back_inserter<ContainerOut>(ys); | |
std::transform(std::begin(xs), std::end(xs), it, f); | |
return ys; | |
} | |
// API search type: transform_inner : ((a -> b), [[a]]) -> [[b]] | |
// fwd bind count: 1 | |
// Applies a function to the elements of the inner containers | |
// of a nested sequence. | |
// transform_inner((*2), [[1, 3, 4], [1, 2]]) == [[2, 6, 8], [2, 4]] | |
// Also known as transform_nested, map_nested or map_inner. | |
template <typename F, | |
typename ContainerIn, | |
typename ContainerOut = typename internal::same_cont_new_t< | |
ContainerIn, | |
typename internal::same_cont_new_t_from_unary_f< | |
typename ContainerIn::value_type, | |
F, | |
0>::type, | |
0>::type> | |
ContainerOut transform_inner(F f, const ContainerIn& xs) { | |
internal::trigger_static_asserts< | |
internal::unary_function_tag, F, | |
typename ContainerIn::value_type::value_type>(); | |
return fplus::transform( | |
fplus::bind_1st_of_2( | |
fplus::transform<F, const typename ContainerIn::value_type&>, f), | |
xs); | |
} | |
namespace internal { | |
template <typename Container> | |
Container reverse(internal::reuse_container_t, Container&& xs) { | |
static_assert(internal::has_order<Container>::value, | |
"Reverse: Container has no order."); | |
std::reverse(std::begin(xs), std::end(xs)); | |
return std::forward<Container>(xs); | |
} | |
template <typename Container> | |
Container reverse(internal::create_new_container_t, const Container& xs) { | |
static_assert(internal::has_order<Container>::value, | |
"Reverse: Container has no order."); | |
Container ys = xs; | |
std::reverse(std::begin(ys), std::end(ys)); | |
return ys; | |
} | |
} // namespace internal | |
// API search type: reverse : [a] -> [a] | |
// fwd bind count: 0 | |
// Reverse a sequence. | |
// reverse([0,4,2,6]) == [6,2,4,0] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut reverse(Container&& xs) { | |
return internal::reverse(internal::can_reuse_v<Container>{}, | |
std::forward<Container>(xs)); | |
} | |
// API search type: take : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Return the first n elements of a sequence xs. | |
// In case n >= length(xs), xs is returned. | |
// take(3, [0,1,2,3,4,5,6,7]) == [0,1,2] | |
// take(10, [0,1,2]) == [0,1,2] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut take(std::size_t amount, Container&& xs) { | |
if (amount >= size_of_cont(xs)) | |
return xs; | |
return get_segment(0, amount, std::forward<Container>(xs)); | |
} | |
// API search type: take_exact : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Return exactly the first n elements of a sequence xs. | |
// Unsafe! Crashes then sequence is too short. | |
// take_exact(3, [0,1,2,3,4,5,6,7]) == [0,1,2] | |
// take_exact(10, [0,1,2]) == crash | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut take_exact(std::size_t amount, Container&& xs) { | |
return get_segment(0, amount, std::forward<Container>(xs)); | |
} | |
// API search type: take_cyclic : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Takes n elements from a sequence considering it as cyclic. | |
// take_cyclic(5, [0,1,2,3]) == [0,1,2,3,0] | |
// take_cyclic(7, [0,1,2,3]) == [0,1,2,3,0,1,2] | |
// take_cyclic(7, [0,1]) == [0,1,0,1,0,1,0] | |
// take_cyclic(2, [0,1,2,3]) == [0,1] | |
// take_cyclic(3, [0]) == [0,0,0] | |
// take_cyclic(3, []) == crash! | |
// Also known as take_wrap. | |
// xs must be non-empty. | |
template <typename Container> | |
Container take_cyclic(std::size_t amount, const Container& xs) { | |
assert(!xs.empty()); | |
Container ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it_out = internal::get_back_inserter(ys); | |
auto it_in = std::begin(xs); | |
while (amount != 0) { | |
*it_out = *it_in; | |
--amount; | |
++it_in; | |
if (it_in == std::end(xs)) { | |
it_in = std::begin(xs); | |
} | |
} | |
return ys; | |
} | |
// API search type: drop : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Skip the first n elements of a sequence xs. | |
// If n > length(xs) an empty sequence is returned. | |
// drop(3, [0,1,2,3,4,5,6,7]) == [3,4,5,6,7] | |
// Also known as skip. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut drop(std::size_t amount, Container&& xs) { | |
if (amount >= size_of_cont(xs)) | |
return ContainerOut(); | |
return get_segment(amount, size_of_cont(xs), std::forward<Container>(xs)); | |
} | |
// API search type: take_last : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Return the last n elements of a sequence xs. | |
// In case n >= length(xs), xs is returned. | |
// take_last(3, [0,1,2,3,4,5,6,7]) == [5,6,7] | |
// take_last(10, [0,1,2]) == [0,1,2] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut take_last(std::size_t amount, Container&& xs) { | |
if (amount >= size_of_cont(xs)) | |
return xs; | |
return drop(size_of_cont(xs) - amount, std::forward<Container>(xs)); | |
} | |
// API search type: drop_last : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Skip the last n elements of a sequence xs. | |
// If n > length(xs) an empty sequence is returned. | |
// drop_last(3, [0,1,2,3,4,5,6,7]) == [0,1,2,3,4] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut drop_last(std::size_t amount, Container&& xs) { | |
if (amount >= size_of_cont(xs)) | |
return ContainerOut(); | |
return take(size_of_cont(xs) - amount, std::forward<Container>(xs)); | |
} | |
// API search type: drop_exact : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Skip exactly the first n elements of a sequence xs. | |
// Unsafe! Crashes when xs is too short. | |
// drop_exact(3, [0,1,2,3,4,5,6,7]) == [3,4,5,6,7] | |
// drop_exact(10, [0,1,2,3,4,5,6,7]) == crash | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut drop_exact(std::size_t amount, Container&& xs) { | |
return get_segment(amount, size_of_cont(xs), std::forward<Container>(xs)); | |
} | |
// API search type: take_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Take elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// take_while(is_even, [0,2,4,5,6,7,8]) == [0,2,4] | |
template <typename Container, typename UnaryPredicate> | |
Container take_while(UnaryPredicate pred, const Container& xs) { | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
auto itFirst = std::find_if(std::begin(xs), std::end(xs), logical_not(pred)); | |
if (itFirst == std::end(xs)) | |
return xs; | |
return Container(std::begin(xs), itFirst); | |
} | |
// API search type: take_last_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Take elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// take_last_while(is_even, [0,2,7,5,6,4,8]) == [6,4,8] | |
template <typename Container, typename UnaryPredicate> | |
Container take_last_while(UnaryPredicate pred, const Container& xs) { | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
const auto r_begin = internal::make_reverse_iterator(std::end(xs)); | |
const auto r_end = internal::make_reverse_iterator(std::begin(xs)); | |
const auto itFirstReverse = std::find_if(r_begin, r_end, logical_not(pred)); | |
if (itFirstReverse == r_begin) | |
return Container(); | |
if (itFirstReverse == r_end) | |
return xs; | |
return Container(itFirstReverse.base(), std::end(xs)); | |
} | |
// API search type: drop_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Remove elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// drop_while(is_even, [0,2,4,5,6,7,8]) == [5,6,7,8] | |
// Also known as trim_left_by. | |
template <typename Container, typename UnaryPredicate> | |
Container drop_while(UnaryPredicate pred, const Container& xs) { | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
auto itFirstNot = std::find_if_not(std::begin(xs), std::end(xs), pred); | |
if (itFirstNot == std::end(xs)) | |
return Container(); | |
return Container(itFirstNot, std::end(xs)); | |
} | |
// API search type: drop_last_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Remove elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// drop_last_while(is_even, [0,2,7,5,6,4,8]) == [0,2,7,5] | |
template <typename Container, typename UnaryPredicate> | |
Container drop_last_while(UnaryPredicate pred, const Container& xs) { | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
const auto r_begin = internal::make_reverse_iterator(std::end(xs)); | |
const auto r_end = internal::make_reverse_iterator(std::begin(xs)); | |
const auto itFirstNotReverse = std::find_if_not(r_begin, r_end, pred); | |
if (itFirstNotReverse == r_begin) | |
return xs; | |
if (itFirstNotReverse == r_end) | |
return Container(); | |
return Container(std::begin(xs), itFirstNotReverse.base()); | |
} | |
// API search type: fold_left : (((a, b) -> a), a, [b]) -> a | |
// fwd bind count: 2 | |
// fold_left((+), 0, [1, 2, 3]) == ((0+1)+2)+3 == 6 | |
// Takes the second argument and the first item of the list | |
// and applies the function to them, | |
// then feeds the function with this result and the second argument and so on. | |
template <typename F, typename Container, typename Acc> | |
Acc fold_left(F f, const Acc& init, const Container& xs) { | |
using std::begin; | |
using std::end; | |
return internal::accumulate(begin(xs), end(xs), init, f); | |
} | |
// API search type: reduce : (((a, a) -> a), a, [a]) -> a | |
// fwd bind count: 2 | |
// reduce((+), 0, [1, 2, 3]) == (0+1+2+3) == 6 | |
// Combines the initial value and all elements of the sequence | |
// using the given function. | |
// The set of f, init and value_type should form a monoid. | |
template <typename F, typename Container> | |
typename Container::value_type | |
reduce(F f, const typename Container::value_type& init, const Container& xs) { | |
return fold_left(f, init, xs); | |
} | |
// API search type: fold_left_1 : (((a, a) -> a), [a]) -> a | |
// fwd bind count: 1 | |
// fold_left_1((+), [1, 2, 3]) == (1+2)+3 == 6 | |
// Takes the first 2 items of the list and applies the function to them, | |
// then feeds the function with this result and the third argument and so on. | |
// xs must be non-empty. | |
template <typename F, typename Container> | |
auto fold_left_1(F f, const Container& xs) { | |
assert(!xs.empty()); | |
using std::begin; | |
using std::end; | |
const auto it = begin(xs); | |
return internal::accumulate(std::next(it), end(xs), *it, f); | |
} | |
// API search type: reduce_1 : (((a, a) -> a), [a]) -> a | |
// fwd bind count: 1 | |
// reduce_1((+), [1, 2, 3]) == (1+2+3) == 6 | |
// Joins all elements of the sequence using the given function. | |
// The set of f and value_type should form a semigroup. | |
template <typename F, typename Container> | |
typename Container::value_type reduce_1(F f, const Container& xs) { | |
assert(!xs.empty()); | |
return fold_left_1(f, xs); | |
} | |
// API search type: fold_right : (((a, b) -> b), b) -> [a] -> b | |
// fwd bind count: 2 | |
// fold_right((+), 0, [1, 2, 3]) == 1+(2+(3+0)) == 6 | |
// Takes the second argument and the last item of the list | |
// and applies the function, | |
// then it takes the penultimate item from the end and the result, and so on. | |
template <typename F, typename Container, typename Acc> | |
Acc fold_right(F f, const Acc& init, const Container& xs) { | |
return internal::accumulate(xs.rbegin(), xs.rend(), init, flip(f)); | |
} | |
// API search type: fold_right_1 : (((a, a) -> a), [a]) -> a | |
// fwd bind count: 1 | |
// fold_right_1((+), [1, 2, 3]) == 1+(2+3)) == 6 | |
// Takes the last two items of the list and applies the function, | |
// then it takes the third item from the end and the result, and so on. | |
template <typename F, typename Container> | |
auto fold_right_1(F f, const Container& xs) { | |
assert(!xs.empty()); | |
const auto it = xs.rbegin(); | |
return internal::accumulate(std::next(it), xs.rend(), *it, flip(f)); | |
} | |
// API search type: scan_left : (((a, b) -> a), a, [b]) -> [a] | |
// fwd bind count: 2 | |
// scan_left((+), 0, [1, 2, 3]) == [0, 1, 3, 6] | |
// Takes the second argument and the first item of the list | |
// and applies the function to them, | |
// then feeds the function with this result and the second argument and so on. | |
// It returns the list of intermediate and final results. | |
template <typename F, typename ContainerIn, typename Acc> | |
auto scan_left(F f, const Acc& init, const ContainerIn& xs) { | |
using ContainerOut = | |
typename internal::same_cont_new_t<ContainerIn, Acc, 1>::type; | |
ContainerOut result; | |
internal::prepare_container(result, size_of_cont(xs) + 1); | |
using std::begin; | |
using std::end; | |
internal::scan_impl(f, init, internal::get_back_inserter(result), begin(xs), | |
end(xs)); | |
return result; | |
} | |
// API search type: scan_left_1 : (((a, a) -> a), [a]) -> [a] | |
// fwd bind count: 1 | |
// scan_left_1((+), [1, 2, 3]) == [1, 3, 6] | |
// Takes the first 2 items of the list and applies the function to them, | |
// then feeds the function with this result and the third argument and so on. | |
// It returns the list of intermediate and final results. | |
// xs must be non-empty. | |
template <typename F, typename ContainerIn> | |
auto scan_left_1(F f, const ContainerIn& xs) { | |
assert(!xs.empty()); | |
using std::begin; | |
using std::end; | |
const auto beginIt = begin(xs); | |
using ContainerOut = typename internal::same_cont_new_t< | |
ContainerIn, internal::uncvref_t<decltype(*beginIt)>, 0>::type; | |
ContainerOut result; | |
internal::prepare_container(result, size_of_cont(xs)); | |
internal::scan_impl(f, *beginIt, internal::get_back_inserter(result), | |
std::next(beginIt), end(xs)); | |
return result; | |
} | |
// API search type: scan_right : (((a, b) -> b), b, [a]) -> [b] | |
// fwd bind count: 2 | |
// scan_right((+), 0, [1, 2, 3]) == [6, 5, 3, 0] | |
// Takes the second argument and the last item of the list | |
// and applies the function, | |
// then it takes the penultimate item from the end and the result, and so on. | |
// It returns the list of intermediate and final results. | |
template < | |
typename F, | |
typename ContainerIn, | |
typename Acc = typename utils::function_traits<F>::template arg<1>::type, | |
typename ContainerOut = | |
typename internal::same_cont_new_t<ContainerIn, Acc, 1>::type> | |
ContainerOut scan_right(F f, const Acc& init, const ContainerIn& xs) { | |
return reverse(scan_left(flip(f), init, reverse(xs))); | |
} | |
// API search type: scan_right_1 : (((a, a) -> a), [a]) -> [a] | |
// fwd bind count: 1 | |
// scan_right_1((+), [1, 2, 3]) == [6, 5, 3] | |
// Takes the last two items of the list and applies the function, | |
// then it takes the third item from the end and the result, and so on. | |
// It returns the list of inntermediate and final results. | |
template <typename F, | |
typename ContainerIn, | |
typename Acc = typename ContainerIn::value_type, | |
typename ContainerOut = | |
typename internal::same_cont_new_t<ContainerIn, Acc, 0>::type> | |
ContainerOut scan_right_1(F f, const ContainerIn& xs) { | |
return reverse(scan_left_1(flip(f), reverse(xs))); | |
} | |
// API search type: sum : [a] -> a | |
// fwd bind count: 0 | |
// Adds up all values in a sequence. | |
// sum([0,3,1]) == 4 | |
// sum([]) == 0 | |
template <typename Container, typename T = typename Container::value_type> | |
T sum(const Container& xs) { | |
T result = T(); | |
for (const auto& x : xs) { | |
result = result + x; | |
} | |
return result; | |
} | |
// API search type: product : [a] -> a | |
// fwd bind count: 0 | |
// Returns the product of all values in a sequence. | |
// product([3,1,2]) == 6 | |
// product([]) == 1 | |
template <typename Container, typename T = typename Container::value_type> | |
T product(const Container& xs) { | |
T result{1}; | |
for (const auto& x : xs) { | |
result = result * x; | |
} | |
return result; | |
} | |
namespace internal { | |
template <typename T, typename Container> | |
Container append_elem(internal::reuse_container_t, const T& y, Container&& xs) { | |
*internal::get_back_inserter(xs) = y; | |
return std::forward<Container>(xs); | |
} | |
template <typename T, typename Container> | |
Container append_elem(internal::create_new_container_t, | |
const T& y, | |
const Container& xs) { | |
Container result; | |
internal::prepare_container(result, size_of_cont(xs) + 1); | |
std::copy(std::begin(xs), std::end(xs), internal::get_back_inserter(result)); | |
*internal::get_back_inserter(result) = y; | |
return result; | |
} | |
} // namespace internal | |
// API search type: append_elem : (a, [a]) -> [a] | |
// fwd bind count: 1 | |
// Extends a sequence with one element at the back. | |
// append_elem([1, 2], 3) == [1, 2, 3] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>, | |
typename T = typename ContainerOut::value_type> | |
ContainerOut append_elem(const T& y, Container&& xs) { | |
return internal::append_elem(internal::can_reuse_v<Container>{}, y, | |
std::forward<Container>(xs)); | |
} | |
namespace internal { | |
template <typename T> | |
std::list<T> prepend_elem(internal::reuse_container_t, | |
const T& y, | |
std::list<T>&& xs) { | |
xs.push_front(y); | |
return std::forward<std::list<T>>(xs); | |
} | |
template <typename T, typename Container> | |
Container prepend_elem(internal::reuse_container_t, | |
const T& y, | |
Container&& xs) { | |
xs.resize(size_of_cont(xs) + 1); | |
std::copy(++xs.rbegin(), xs.rend(), xs.rbegin()); | |
*std::begin(xs) = y; | |
return std::forward<Container>(xs); | |
} | |
template <typename T, typename Container> | |
Container prepend_elem(internal::create_new_container_t, | |
const T& y, | |
const Container& xs) { | |
Container result; | |
internal::prepare_container(result, size_of_cont(xs) + 1); | |
*internal::get_back_inserter(result) = y; | |
std::copy(std::begin(xs), std::end(xs), internal::get_back_inserter(result)); | |
return result; | |
} | |
} // namespace internal | |
// API search type: prepend_elem : (a, [a]) -> [a] | |
// fwd bind count: 1 | |
// Extends a sequence with one element in the front. | |
// prepend_elem([2, 3], 1) == [1, 2, 3] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>, | |
typename T = typename ContainerOut::value_type> | |
ContainerOut prepend_elem(const T& y, Container&& xs) { | |
return internal::prepend_elem(internal::can_reuse_v<Container>{}, y, | |
std::forward<Container>(xs)); | |
} | |
// API search type: append : ([a], [a]) -> [a] | |
// fwd bind count: 1 | |
// Concatenates two sequences. | |
// append([1, 2], [3, 4, 5]) == [1, 2, 3, 4, 5] | |
template <typename ContainerIn1, | |
typename ContainerIn2 = ContainerIn1, | |
typename ContainerOut = ContainerIn1> | |
ContainerOut append(const ContainerIn1& xs, const ContainerIn2& ys) { | |
ContainerOut result; | |
internal::prepare_container(result, size_of_cont(xs) + size_of_cont(ys)); | |
std::copy(std::begin(xs), std::end(xs), internal::get_back_inserter(result)); | |
std::copy(std::begin(ys), std::end(ys), internal::get_back_inserter(result)); | |
return result; | |
} | |
// API search type: append_convert : ([a], [a]) -> [a] | |
// fwd bind count: 1 | |
// Same as append, but makes it easier to | |
// use an output container type different from the input container type. | |
template <typename ContainerOut, | |
typename ContainerIn1, | |
typename ContainerIn2 = ContainerIn1> | |
ContainerOut append_convert(const ContainerIn1& xs, const ContainerIn2& ys) { | |
return append<ContainerIn1, ContainerIn2, ContainerOut>(xs, ys); | |
} | |
// API search type: concat : [[a]] -> [a] | |
// fwd bind count: 0 | |
// Concatenates multiple sequences. | |
// concat([[1, 2], [], [3]]) == [1, 2, 3] | |
// Also known as flatten. | |
template <typename ContainerIn, | |
typename ContainerOut = typename ContainerIn::value_type> | |
ContainerOut concat(const ContainerIn& xss) { | |
std::size_t length = | |
sum(transform(size_of_cont<typename ContainerIn::value_type>, xss)); | |
ContainerOut result; | |
internal::prepare_container(result, length); | |
using std::begin; | |
using std::end; | |
for (const auto& xs : xss) { | |
result.insert(end(result), begin(xs), end(xs)); | |
} | |
return result; | |
} | |
// API search type: interweave : ([a], [a]) -> [a] | |
// fwd bind count: 1 | |
// Return a sequence that contains elements from the two provided sequences | |
// in alternating order. If one list runs out of items, | |
// appends the items from the remaining list. | |
// interweave([1,3], [2,4]) == [1,2,3,4] | |
// interweave([1,3,5,7], [2,4]) == [1,2,3,4,5,7] | |
// See interleave for interweaving more than two sequences. | |
template <typename Container> | |
Container interweave(const Container& xs, const Container& ys) { | |
Container result; | |
internal::prepare_container(result, size_of_cont(xs) + size_of_cont(ys)); | |
auto it = internal::get_back_inserter<Container>(result); | |
auto it_xs = std::begin(xs); | |
auto it_ys = std::begin(ys); | |
while (it_xs != std::end(xs) || it_ys != std::end(ys)) { | |
if (it_xs != std::end(xs)) | |
*it = *(it_xs++); | |
if (it_ys != std::end(ys)) | |
*it = *(it_ys++); | |
} | |
return result; | |
} | |
// API search type: unweave : [a] -> ([a], [a]) | |
// fwd bind count: 0 | |
// Puts the elements with an even index into the first list, | |
// and the elements with an odd index into the second list. | |
// Inverse of interweave. | |
// unweave([0,1,2,3]) == ([0,2], [1,3]) | |
// unweave([0,1,2,3,4]) == ([0,2,4], [1,3]) | |
template <typename Container> | |
std::pair<Container, Container> unweave(const Container& xs) { | |
std::pair<Container, Container> result; | |
if (is_even(size_of_cont(xs))) | |
internal::prepare_container(result.first, size_of_cont(xs) / 2); | |
else | |
internal::prepare_container(result.first, size_of_cont(xs) / 2 + 1); | |
internal::prepare_container(result.second, size_of_cont(xs) / 2); | |
auto it_even = internal::get_back_inserter<Container>(result.first); | |
auto it_odd = internal::get_back_inserter<Container>(result.second); | |
std::size_t counter = 0; | |
for (const auto& x : xs) { | |
if (counter++ % 2 == 0) | |
*it_even = x; | |
else | |
*it_odd = x; | |
} | |
return result; | |
} | |
namespace internal { | |
template <typename Compare, typename T> | |
std::list<T> sort_by(internal::reuse_container_t, | |
Compare comp, | |
std::list<T>&& xs) { | |
xs.sort(comp); | |
return std::forward<std::list<T>>(xs); | |
} | |
template <typename Compare, typename T> | |
std::list<T> sort_by(internal::create_new_container_t, | |
Compare comp, | |
const std::list<T>& xs) { | |
auto result = xs; | |
result.sort(comp); | |
return result; | |
} | |
template <typename Compare, typename Container> | |
Container sort_by(internal::reuse_container_t, Compare comp, Container&& xs) { | |
std::sort(std::begin(xs), std::end(xs), comp); | |
return std::forward<Container>(xs); | |
} | |
template <typename Compare, typename Container> | |
Container sort_by(internal::create_new_container_t, | |
Compare comp, | |
const Container& xs) { | |
auto result = xs; | |
std::sort(std::begin(result), std::end(result), comp); | |
return result; | |
} | |
} // namespace internal | |
// API search type: sort_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence by given less comparator. | |
template <typename Compare, | |
typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut sort_by(Compare comp, Container&& xs) { | |
return internal::sort_by(internal::can_reuse_v<Container>{}, comp, | |
std::forward<Container>(xs)); | |
} | |
namespace internal { | |
// workarounds for clang bug 24115 | |
// (std::sort and std::unique with std::function as comp) | |
// https://llvm.org/bugs/show_bug.cgi?id=24115 | |
template <typename F> | |
struct is_less_by_struct { | |
is_less_by_struct(F f) : f_(f){}; | |
template <typename T> | |
bool operator()(const T& x, const T& y) { | |
return f_(x) < f_(y); | |
} | |
private: | |
F f_; | |
}; | |
template <typename F> | |
struct is_equal_by_struct { | |
is_equal_by_struct(F f) : f_(f){}; | |
template <typename T> | |
bool operator()(const T& x, const T& y) { | |
return f_(x) == f_(y); | |
} | |
private: | |
F f_; | |
}; | |
} // namespace internal | |
// API search type: sort_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence by a given transformer. | |
template <typename F, | |
typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut sort_on(F f, Container&& xs) { | |
return sort_by(internal::is_less_by_struct<F>(f), | |
std::forward<Container>(xs)); | |
} | |
// API search type: sort : [a] -> [a] | |
// fwd bind count: 0 | |
// Sort a sequence to ascending order using std::less. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut sort(Container&& xs) { | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return sort_by(std::less<T>(), std::forward<Container>(xs)); | |
} | |
namespace internal { | |
template <typename Compare, typename T> | |
std::list<T> stable_sort_by(internal::reuse_container_t, | |
Compare comp, | |
std::list<T>&& xs) { | |
xs.sort(comp); // std::list<T>::sort ist already stable. | |
return std::forward<std::list<T>>(xs); | |
} | |
template <typename Compare, typename T> | |
std::list<T> stable_sort_by(internal::create_new_container_t, | |
Compare comp, | |
const std::list<T>& xs) { | |
auto result = xs; | |
result.sort(comp); // std::list<T>::sort ist already stable. | |
return result; | |
} | |
template <typename Compare, typename Container> | |
Container stable_sort_by(internal::reuse_container_t, | |
Compare comp, | |
Container&& xs) { | |
std::sort(std::begin(xs), std::end(xs), comp); | |
return std::forward<Container>(xs); | |
} | |
template <typename Compare, typename Container> | |
Container stable_sort_by(internal::create_new_container_t, | |
Compare comp, | |
const Container& xs) { | |
auto result = xs; | |
std::sort(std::begin(result), std::end(result), comp); | |
return result; | |
} | |
} // namespace internal | |
// API search type: stable_sort_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence stably by given less comparator. | |
template <typename Compare, | |
typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut stable_sort_by(Compare comp, Container&& xs) { | |
return internal::stable_sort_by(internal::can_reuse_v<Container>{}, comp, | |
std::forward<Container>(xs)); | |
} | |
// API search type: stable_sort_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence stably by given transformer. | |
template <typename F, | |
typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut stable_sort_on(F f, Container&& xs) { | |
return stable_sort_by(internal::is_less_by_struct<F>(f), | |
std::forward<Container>(xs)); | |
} | |
// API search type: stable_sort : [a] -> [a] | |
// fwd bind count: 0 | |
// Sort a sequence stably to ascending order using std::less. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut stable_sort(Container&& xs) { | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return stable_sort_by(std::less<T>(), std::forward<Container>(xs)); | |
} | |
namespace internal { | |
template <typename Compare, typename Container> | |
Container partial_sort_by(internal::reuse_container_t, | |
Compare comp, | |
std::size_t count, | |
Container&& xs) { | |
if (count > xs.size()) { | |
count = xs.size(); | |
} | |
auto middle = std::begin(xs); | |
internal::advance_iterator(middle, count); | |
std::partial_sort(std::begin(xs), middle, std::end(xs), comp); | |
return std::forward<Container>( | |
get_segment(internal::reuse_container_t(), 0, count, xs)); | |
} | |
template <typename Compare, typename Container> | |
Container partial_sort_by(internal::create_new_container_t, | |
Compare comp, | |
std::size_t count, | |
const Container& xs) { | |
auto result = xs; | |
return partial_sort_by(internal::reuse_container_t(), comp, count, | |
std::move(result)); | |
} | |
} // namespace internal | |
// API search type: partial_sort_by : (((a, a) -> Bool), Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Partially sort a sequence by a given less comparator. | |
// Returns only the sorted segment. | |
template <typename Compare, | |
typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut partial_sort_by(Compare comp, std::size_t count, Container&& xs) { | |
return internal::partial_sort_by(internal::can_reuse_v<Container>{}, comp, | |
count, std::forward<Container>(xs)); | |
} | |
// API search type: partial_sort_on : ((a -> b), Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Partially sort a sequence by a given transformer. | |
// Returns only the sorted segment. | |
template <typename F, | |
typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut partial_sort_on(F f, std::size_t count, Container&& xs) { | |
return partial_sort_by(internal::is_less_by_struct<F>(f), count, | |
std::forward<Container>(xs)); | |
} | |
// API search type: partial_sort : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Partially sort a sequence in ascending order using std::less. | |
// Returns only the sorted segment. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut partial_sort(std::size_t count, Container&& xs) { | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return partial_sort_by(std::less<T>(), count, std::forward<Container>(xs)); | |
} | |
// API search type: nth_element_by : (((a, a) -> Bool), Int, [a]) -> a | |
// fwd bind count: 2 | |
// Return the nth largest element of a sequence by a given less comparator. | |
template <typename Compare, | |
typename Container, | |
typename T = typename Container::value_type> | |
T nth_element_by(Compare comp, std::size_t n, const Container& xs) { | |
assert(n < xs.size()); | |
auto result = xs; | |
auto middle = std::begin(result); | |
internal::advance_iterator(middle, n); | |
std::nth_element(std::begin(result), middle, std::end(result), comp); | |
return *middle; | |
} | |
// API search type: nth_element_on : ((a -> b), Int, [a]) -> a | |
// fwd bind count: 2 | |
// Return the nth largest element of a sequence by a given transformer. | |
template <typename F, | |
typename Container, | |
typename T = typename Container::value_type> | |
T nth_element_on(F f, std::size_t n, const Container& xs) { | |
return nth_element_by(internal::is_less_by_struct<F>(f), n, xs); | |
} | |
// API search type: nth_element : (Int, [a]) -> a | |
// fwd bind count: 1 | |
// Return the nth largest element of a sequence using std::less. | |
template <typename Container, typename T = typename Container::value_type> | |
T nth_element(std::size_t n, const Container& xs) { | |
return nth_element_by(std::less<T>(), n, xs); | |
} | |
namespace internal { | |
template <typename BinaryPredicate, typename Container> | |
Container unique_by(internal::reuse_container_t, | |
BinaryPredicate pred, | |
Container&& xs) { | |
internal::check_binary_predicate_for_container<BinaryPredicate, Container>(); | |
const auto it_end = std::unique(std::begin(xs), std::end(xs), pred); | |
xs.erase(it_end, std::end(xs)); | |
return std::forward<Container>(xs); | |
} | |
template <typename BinaryPredicate, typename Container> | |
Container unique_by(internal::create_new_container_t, | |
BinaryPredicate pred, | |
const Container& xs) { | |
auto result = xs; | |
return unique_by(internal::reuse_container_t(), pred, std::move(result)); | |
} | |
} // namespace internal | |
// API search type: unique_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Like unique, eliminates all but the first element | |
// from every consecutive group of equivalent elements from the sequence; | |
// but with a user supplied equality predicate. | |
// See nub_by for making elements globally unique in a sequence. | |
// O(n) | |
template <typename BinaryPredicate, | |
typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut unique_by(BinaryPredicate pred, Container&& xs) { | |
return internal::unique_by(internal::can_reuse_v<Container>{}, pred, | |
std::forward<Container>(xs)); | |
} | |
// API search type: unique_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Like unique, eliminates all but the first element | |
// from every consecutive group of equivalent elements from the sequence; | |
// but with a user supplied transformation (e.g. getter). | |
// See nub_on for making elements globally unique in a sequence. | |
// O(n) | |
// Also known as drop_repeats. | |
template <typename F, | |
typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut unique_on(F f, Container&& xs) { | |
return unique_by(internal::is_equal_by_struct<F>(f), | |
std::forward<Container>(xs)); | |
} | |
// API search type: unique : [a] -> [a] | |
// fwd bind count: 0 | |
// Eliminates all but the first element | |
// from every consecutive group of equivalent elements from the sequence. | |
// unique([1,2,2,3,2]) == [1,2,3,2] | |
// See nub for making elements globally unique in a sequence. | |
// O(n) | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut unique(Container&& xs) { | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return unique_on(identity<T>, std::forward<Container>(xs)); | |
} | |
// API search type: intersperse : (a, [a]) -> [a] | |
// fwd bind count: 1 | |
// Insert a value between all adjacent values in a sequence. | |
// intersperse(0, [1, 2, 3]) == [1, 0, 2, 0, 3] | |
// Also known as interpose. | |
template <typename Container, typename X = typename Container::value_type> | |
Container intersperse(const X& value, const Container& xs) { | |
if (xs.empty()) | |
return Container(); | |
if (size_of_cont(xs) == 1) | |
return xs; | |
Container result; | |
internal::prepare_container( | |
result, std::max<std::size_t>(0, size_of_cont(xs) * 2 - 1)); | |
auto it = internal::get_back_inserter(result); | |
for_each(std::begin(xs), --std::end(xs), [&value, &it](const X& x) { | |
*it = x; | |
*it = value; | |
}); | |
*it = xs.back(); | |
return result; | |
} | |
// API search type: join : ([a], [[a]]) -> [a] | |
// fwd bind count: 1 | |
// Inserts a separator sequence in between the elements | |
// of a sequence of sequences and concatenates the result. | |
// Also known as intercalate or implode. | |
// join(", ", ["a", "bee", "cee"]) == "a, bee, cee" | |
// join([0, 0], [[1], [2], [3, 4]]) == [1, 0, 0, 2, 0, 0, 3, 4] | |
template <typename Container, typename X = typename Container::value_type> | |
X join(const X& separator, const Container& xs) { | |
return concat(intersperse(separator, xs)); | |
} | |
// API search type: join_elem : (a, [[a]]) -> [a] | |
// fwd bind count: 1 | |
// Inserts a separator in between the elements | |
// of a sequence of sequences and concatenates the result. | |
// Also known as intercalate_elem. | |
// join_elem(';', ["a", "bee", "cee"]) == "a;bee;cee" | |
// join_elem(0, [[1], [2], [3, 4]]) == [1, 0, 2, 0, 3, 4]] | |
template <typename Container, | |
typename Inner = typename Container::value_type, | |
typename X = typename Inner::value_type> | |
Inner join_elem(const X& separator, const Container& xs) { | |
return join(Inner(1, separator), xs); | |
} | |
// API search type: is_elem_of_by : ((a -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if at least one element of the container fulfils a predicate. | |
// is_elem_of_by((==), [1,2,3]) == true | |
template <typename UnaryPredicate, typename Container> | |
bool is_elem_of_by(UnaryPredicate pred, const Container& xs) { | |
return std::find_if(std::begin(xs), std::end(xs), pred) != std::end(xs); | |
} | |
// API search type: is_elem_of : (a, [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if an element is a member of a container. | |
// is_elem_of(2, [1,2,3]) == true | |
// Also known as flip(contains). | |
template <typename Container> | |
bool is_elem_of(const typename Container::value_type& x, const Container& xs) { | |
return is_elem_of_by(is_equal_to(x), xs); | |
} | |
// API search type: nub_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Makes the elements in a container unique with respect to a predicate | |
// nub_by((==), [1,2,2,3,2]) == [1,2,3] | |
// O(n^2) | |
template <typename Container, typename BinaryPredicate> | |
Container nub_by(BinaryPredicate p, const Container& xs) { | |
Container result; | |
auto itOut = internal::get_back_inserter(result); | |
for (const auto& x : xs) { | |
auto eqToX = bind_1st_of_2(p, x); | |
if (!is_elem_of_by(eqToX, result)) { | |
*itOut = x; | |
} | |
} | |
return result; | |
} | |
// API search type: nub_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Makes the elements in a container unique | |
// with respect to their function value. | |
// nub_on((mod 10), [12,32,15]) == [12,15] | |
// O(n^2) | |
template <typename Container, typename F> | |
Container nub_on(F f, const Container& xs) { | |
return nub_by(is_equal_by(f), xs); | |
} | |
// API search type: nub : [a] -> [a] | |
// fwd bind count: 0 | |
// Makes the elements in a container unique. | |
// nub([1,2,2,3,2]) == [1,2,3] | |
// O(n^2) | |
// Also known as distinct. | |
template <typename Container> | |
Container nub(const Container& xs) { | |
typedef typename Container::value_type T; | |
return nub_by(std::equal_to<T>(), xs); | |
} | |
// API search type: all_unique_by_eq : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all elements in a container are unique | |
// with respect to a predicate. | |
// Returns true for empty containers. | |
// O(n^2) | |
template <typename Container, typename BinaryPredicate> | |
bool all_unique_by_eq(BinaryPredicate p, const Container& xs) { | |
internal::check_binary_predicate_for_container<BinaryPredicate, Container>(); | |
return size_of_cont(nub_by(p, xs)) == size_of_cont(xs); | |
} | |
// API search type: all_unique_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all elements in a container are unique | |
// with respect to their function values. | |
// Returns true for empty containers. | |
// O(n^2) | |
template <typename Container, typename F> | |
bool all_unique_on(F f, const Container& xs) { | |
return all_unique_by_eq(is_equal_by(f), xs); | |
} | |
// API search type: all_unique : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if all elements in a container are unique. | |
// Returns true for empty containers. | |
// O(n^2) | |
template <typename Container> | |
bool all_unique(const Container& xs) { | |
typedef typename Container::value_type T; | |
auto comp = std::equal_to<T>(); | |
return all_unique_by_eq(comp, xs); | |
} | |
// API search type: is_strictly_sorted_by : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is strictly sorted using a predicate. | |
// comp(a, b) must return true only if a < b. | |
// O(n) | |
template <typename Container, typename Compare> | |
bool is_strictly_sorted_by(Compare comp, const Container& xs) { | |
internal::check_compare_for_container<Compare, Container>(); | |
if (size_of_cont(xs) < 2) | |
return true; | |
auto it1 = std::begin(xs); | |
for (auto it2 = it1 + 1; it2 < std::end(xs); ++it1, ++it2) | |
if (!internal::invoke(comp, *it1, *it2)) | |
return false; | |
return true; | |
} | |
// API search type: is_strictly_sorted_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is strictly sorted using a transformer. | |
// O(n) | |
template <typename Container, typename F> | |
bool is_strictly_sorted_on(F f, const Container& xs) { | |
return is_strictly_sorted_by(is_less_by(f), xs); | |
} | |
// API search type: is_strictly_sorted : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if a container already is strictly sorted | |
// in ascending order using std::less. | |
// O(n) | |
template <typename Container> | |
bool is_strictly_sorted(const Container& xs) { | |
typedef typename Container::value_type T; | |
auto comp = std::less<T>(); | |
return is_strictly_sorted_by(comp, xs); | |
} | |
// API search type: is_sorted_by : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is sorted using a predicate. | |
// comp(a, b) must return true only if a < b. | |
// O(n) | |
template <typename Container, typename Compare> | |
bool is_sorted_by(Compare comp, const Container& xs) { | |
internal::check_compare_for_container<Compare, Container>(); | |
if (size_of_cont(xs) < 2) | |
return true; | |
auto it1 = std::begin(xs); | |
for (auto it2 = it1 + 1; it2 < std::end(xs); ++it1, ++it2) | |
if (internal::invoke(comp, *it2, *it1)) | |
return false; | |
return true; | |
} | |
// API search type: is_sorted_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is strictly sorted using a transformer. | |
// O(n) | |
template <typename Container, typename F> | |
bool is_sorted_on(F f, const Container& xs) { | |
return is_sorted_by(is_less_by(f), xs); | |
} | |
// API search type: is_sorted : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if a container already is sorted | |
// in ascending order using std::less. | |
// O(n) | |
template <typename Container> | |
bool is_sorted(const Container& xs) { | |
typedef typename Container::value_type T; | |
auto comp = std::less<T>(); | |
return is_sorted_by(comp, xs); | |
} | |
// API search type: is_prefix_of : ([a], [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a containers starts with a token. | |
// is_prefix_of("Fun", "FunctionalPlus") == true | |
template <typename Container> | |
bool is_prefix_of(const Container& token, const Container& xs) { | |
if (size_of_cont(token) > size_of_cont(xs)) | |
return false; | |
return get_segment(0, size_of_cont(token), xs) == token; | |
} | |
// API search type: is_suffix_of : ([a], [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a containers contains a token as a segment. | |
// is_suffix_of("us", "FunctionalPlus") == true | |
template <typename Container> | |
bool is_suffix_of(const Container& token, const Container& xs) { | |
if (size_of_cont(token) > size_of_cont(xs)) | |
return false; | |
return get_segment(size_of_cont(xs) - size_of_cont(token), size_of_cont(xs), | |
xs) == token; | |
} | |
// API search type: all_by : ((a -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a containers contains a token as a segment. | |
// all_by(is_even, [2, 4, 6]) == true | |
// Returns true for empty containers. | |
template <typename UnaryPredicate, typename Container> | |
bool all_by(UnaryPredicate p, const Container& xs) { | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
return std::all_of(std::begin(xs), std::end(xs), p); | |
} | |
// API search type: all : [Bool] -> Bool | |
// fwd bind count: 0 | |
// Checks if all values in a container evaluate to true. | |
// all([true, false, true]) == false | |
// Returns true for empty containers. | |
template <typename Container> | |
bool all(const Container& xs) { | |
typedef typename Container::value_type T; | |
return all_by(identity<T>, xs); | |
} | |
// API search type: all_the_same_by : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all values in a container are equal using a binary predicate. | |
// Returns true for empty containers. | |
template <typename Container, typename BinaryPredicate> | |
bool all_the_same_by(BinaryPredicate p, const Container& xs) { | |
internal::check_binary_predicate_for_container<BinaryPredicate, Container>(); | |
if (size_of_cont(xs) < 2) | |
return true; | |
auto unaryPredicate = bind_1st_of_2(p, xs.front()); | |
return all_by(unaryPredicate, xs); | |
} | |
// API search type: all_the_same_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all values in a container are equal using a transformer. | |
// Returns true for empty containers. | |
template <typename Container, typename F> | |
bool all_the_same_on(F f, const Container& xs) { | |
if (size_of_cont(xs) < 2) | |
return true; | |
auto unaryPredicate = is_equal_by_to(f, f(xs.front())); | |
return all_by(unaryPredicate, xs); | |
} | |
// API search type: all_the_same : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if all values in a container are equal. | |
// Returns true for empty containers. | |
template <typename Container> | |
bool all_the_same(const Container& xs) { | |
typedef typename Container::value_type T; | |
auto binaryPredicate = std::equal_to<T>(); | |
return all_the_same_by(binaryPredicate, xs); | |
} | |
// API search type: numbers_step : (a, a, a) -> [a] | |
// fwd bind count: 2 | |
// Return a sequence of numbers using a specific step. | |
// numbers_step(2, 9, 2) == [2, 4, 6, 8] | |
template <typename T, typename ContainerOut = std::vector<T>> | |
ContainerOut numbers_step(const T start, const T end, const T step) { | |
ContainerOut result; | |
if ((step > 0 && start >= end) || (step < 0 && start <= end) || step == 0) { | |
return result; | |
} | |
std::size_t size = static_cast<std::size_t>((end - start) / step); | |
internal::prepare_container(result, size); | |
auto it = internal::get_back_inserter<ContainerOut>(result); | |
for (T x = start; x < end; x += step) | |
*it = x; | |
return result; | |
} | |
// API search type: numbers : (a, a) -> [a] | |
// fwd bind count: 1 | |
// Return an ascending sequence of numbers.. | |
// Also known as range. | |
// numbers(2, 9) == [2, 3, 4, 5, 6, 7, 8] | |
template <typename T, typename ContainerOut = std::vector<T>> | |
ContainerOut numbers(const T start, const T end) { | |
return numbers_step<T, ContainerOut>(start, end, 1); | |
} | |
// API search type: singleton_seq : a -> [a] | |
// fwd bind count: 0 | |
// Construct a sequence containing a single value. | |
// singleton_seq(3) == [3]. | |
template <typename T, typename ContainerOut = std::vector<T>> | |
ContainerOut singleton_seq(const T& x) { | |
return ContainerOut(1, x); | |
} | |
// API search type: all_idxs : [a] -> [Int] | |
// fwd bind count: 0 | |
// Returns a vector containing all valid indices of sequence xs. | |
// all_idxs([6,4,7,6]) == [0,1,2,3] | |
template <typename Container> | |
std::vector<std::size_t> all_idxs(const Container& xs) { | |
return numbers<std::size_t>(0, size_of_cont(xs)); | |
} | |
// API search type: init : [a] -> [a] | |
// fwd bind count: 0 | |
// init([0,1,2,3]) == [0,1,2] | |
// Unsafe! xs must be non-empty. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut init(Container&& xs) { | |
assert(!is_empty(xs)); | |
return get_segment(0, size_of_cont(std::forward<Container>(xs)) - 1, xs); | |
} | |
// API search type: tail : [a] -> [a] | |
// fwd bind count: 0 | |
// Drops the first element of a container, keeps the rest. Unsafe! | |
// tail([0,1,2,3]) == [1,2,3] | |
// Unsafe! xs must be non-empty. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut tail(Container&& xs) { | |
assert(!is_empty(xs)); | |
return get_segment(1, size_of_cont(std::forward<Container>(xs)), xs); | |
} | |
// API search type: head : [a] -> a | |
// fwd bind count: 0 | |
// Return the first element of a container. | |
// head([0,1,2,3]) == 0 | |
// Unsafe! xs must be non-empty. | |
template <typename Container> | |
typename Container::value_type head(const Container& xs) { | |
assert(!is_empty(xs)); | |
return xs.front(); | |
} | |
// API search type: last : [a] -> a | |
// fwd bind count: 0 | |
// Return the last element of a container. | |
// last([0,1,2,3]) == 3 | |
// Unsafe! xs must be non-empty. | |
template <typename Container> | |
typename Container::value_type last(const Container& xs) { | |
assert(!is_empty(xs)); | |
return xs.back(); | |
} | |
// API search type: mean_stddev : [a] -> (a, a) | |
// fwd bind count: 0 | |
// Calculates the mean and the population standard deviation. | |
// mean_stddev([4, 8]) == (6, 2) | |
// mean_stddev([1, 3, 7, 4]) == (3.75, 2.5) | |
// xs must be non-empty. | |
template <typename Result, typename Container> | |
std::pair<Result, Result> mean_stddev(const Container& xs) { | |
assert(size_of_cont(xs) != 0); | |
// http://stackoverflow.com/a/7616783/1866775 | |
Result sum = static_cast<Result>(internal::accumulate( | |
xs.begin(), xs.end(), static_cast<typename Container::value_type>(0))); | |
Result mean = sum / static_cast<Result>(xs.size()); | |
std::vector<Result> diff(xs.size()); | |
std::transform(xs.begin(), xs.end(), diff.begin(), | |
[mean](Result x) { return x - mean; }); | |
Result sq_sum = std::inner_product(diff.begin(), diff.end(), diff.begin(), | |
static_cast<Result>(0)); | |
Result stddev = std::sqrt(sq_sum / static_cast<Result>(xs.size())); | |
return std::make_pair(mean, stddev); | |
} | |
// API search type: count_occurrences_by : ((a -> b), [a]) -> Map b Int | |
// fwd bind count: 1 | |
// Returns a discrete frequency distribution of the elements in a container | |
// applying a specific transformer. | |
// count_occurrences_by(floor, [1.1, 2.3, 2.7, 3.6, 2.4]) == [(1, 1), (2, 3), | |
// (3, 1)] O(n) | |
template <typename F, typename ContainerIn> | |
auto count_occurrences_by(F f, const ContainerIn& xs) { | |
using In = typename ContainerIn::value_type; | |
using MapOut = | |
std::map<std::decay_t<internal::invoke_result_t<F, In>>, std::size_t>; | |
internal::trigger_static_asserts<internal::unary_function_tag, F, | |
typename ContainerIn::value_type>(); | |
MapOut result; | |
for (const auto& x : xs) { | |
++result[internal::invoke(f, x)]; | |
} | |
return result; | |
} | |
// API search type: count_occurrences : [a] -> Map a Int | |
// fwd bind count: 0 | |
// Returns a discrete frequency distribution of the elements in a container | |
// applying a specific transformer. | |
// Can be used to create a histogram. | |
// count_occurrences([1,2,2,3,2]) == [(1, 1), (2, 3), (3, 1)] | |
// O(n) | |
template <typename ContainerIn, | |
typename MapOut = | |
typename std::map<typename ContainerIn::value_type, std::size_t>> | |
MapOut count_occurrences(const ContainerIn& xs) { | |
return count_occurrences_by(identity<typename ContainerIn::value_type>, xs); | |
} | |
// API search type: lexicographical_less_by : (((a, a) -> Bool), [a], [a]) -> | |
// Bool fwd bind count: 2 Lexicographical less-than comparison using a specific | |
// predicate. lexicographical_less_by((<), [0,1,2,2,4,5], [0,1,2,3,4,5]) == true | |
// lexicographical_less_by((<), "012245", "012345") == true | |
// lexicographical_less_by((<), "01234", "012345") == true | |
// lexicographical_less_by((<), "012345", "01234") == false | |
// lexicographical_less_by((<), "012345", "012345") == false | |
template <typename Container, typename BinaryPredicate> | |
bool lexicographical_less_by(BinaryPredicate p, | |
const Container& xs, | |
const Container& ys) { | |
internal::check_binary_predicate_for_container<BinaryPredicate, Container>(); | |
auto itXs = std::begin(xs); | |
auto itYs = std::begin(ys); | |
while (itXs != std::end(xs) && itYs != std::end(ys)) { | |
if (internal::invoke(p, *itXs, *itYs)) { | |
return true; | |
} | |
if (internal::invoke(p, *itYs, *itXs)) { | |
return false; | |
} | |
++itXs; | |
++itYs; | |
} | |
if (size_of_cont(xs) < size_of_cont(ys)) { | |
return true; | |
} | |
return false; | |
} | |
// API search type: lexicographical_less : ([a], [a]) -> Bool | |
// fwd bind count: 1 | |
// Lexicographical less-than comparison. | |
// lexicographical_less([0,1,2,2,4,5], [0,1,2,3,4,5]) == true | |
// lexicographical_less("012245", "012345") == true | |
// lexicographical_less("01234", "012345") == true | |
// lexicographical_less("012345", "01234") == false | |
// lexicographical_less("012345", "012345") == false | |
template <typename Container> | |
bool lexicographical_less(const Container& xs, const Container& ys) { | |
return lexicographical_less_by(is_less<typename Container::value_type>, xs, | |
ys); | |
} | |
// API search type: lexicographical_sort : [[a]] -> [[a]] | |
// fwd bind count: 0 | |
// sort by lexicographical_less | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut lexicographical_sort(Container&& xs) { | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return sort_by(lexicographical_less<T>, std::forward<Container>(xs)); | |
} | |
// API search type: replicate : (Int, a) -> [a] | |
// fwd bind count: 1 | |
// Create a sequence containing x n times. | |
// replicate(3, 1) == [1, 1, 1] | |
template <typename T, typename ContainerOut = std::vector<T>> | |
ContainerOut replicate(std::size_t n, const T& x) { | |
return ContainerOut(n, x); | |
} | |
namespace internal { | |
template <typename UnaryPredicate, typename T> | |
T instead_of_if(internal::reuse_container_t, | |
UnaryPredicate pred, | |
const T& alt, | |
T&& x) { | |
if (internal::invoke(pred, x)) | |
return alt; | |
else | |
return std::forward<T>(x); | |
} | |
template <typename UnaryPredicate, typename T> | |
T instead_of_if(internal::create_new_container_t, | |
UnaryPredicate pred, | |
const T& alt, | |
const T& x) { | |
if (internal::invoke(pred, x)) | |
return alt; | |
else | |
return x; | |
} | |
} // namespace internal | |
// API search type: instead_of_if : ((a -> Bool), a, a) -> a | |
// fwd bind count: 2 | |
// Return alt if pred(x), otherwise x itself. | |
template <typename UnaryPredicate, typename T, typename TAlt> | |
auto instead_of_if(UnaryPredicate pred, const TAlt& alt, T&& x) { | |
return internal::instead_of_if(internal::can_reuse_v<T>{}, pred, alt, | |
std::forward<T>(x)); | |
} | |
// API search type: instead_of_if_empty : ((a -> Bool), [a], [a]) -> [a] | |
// fwd bind count: 2 | |
// Return alt if xs is empty, otherwise xs itself. | |
template <typename Container, | |
typename ContainerAlt, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut instead_of_if_empty(const ContainerAlt& alt, Container&& xs) { | |
return instead_of_if(is_empty<internal::remove_const_and_ref_t<Container>>, | |
alt, std::forward<Container>(xs)); | |
} | |
} // namespace fplus |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. | |
// https://github.com/Dobiasd/FunctionalPlus | |
// Distributed under the Boost Software License, Version 1.0. | |
// (See accompanying file LICENSE_1_0.txt or copy at | |
// http://www.boost.org/LICENSE_1_0.txt) | |
#pragma once | |
#include <fplus/compare.hpp> | |
#include <fplus/composition.hpp> | |
#include <fplus/container_traits.hpp> | |
#include <fplus/maybe.hpp> | |
#include <fplus/internal/container_common.hpp> | |
#include <fplus/internal/invoke.hpp> | |
#include <fplus/internal/meta.hpp> | |
#include <algorithm> | |
#include <cassert> | |
#include <cmath> | |
#include <cstddef> | |
#include <iterator> | |
#include <numeric> | |
namespace fplus | |
{ | |
namespace internal | |
{ | |
template <typename UnaryPredicate, typename Container> | |
void | |
check_unary_predicate_for_container () | |
{ | |
internal::check_unary_predicate_for_type<UnaryPredicate, | |
typename Container::value_type> (); | |
} | |
template <typename F, typename Container> | |
void | |
check_index_with_type_predicate_for_container () | |
{ | |
typedef typename Container::value_type T; | |
internal::trigger_static_asserts<internal::binary_function_tag, F, | |
std::size_t, T> (); | |
static_assert ( | |
std::is_convertible<internal::invoke_result_t<F, std::size_t, T>, | |
bool>::value, | |
"Function must return bool."); | |
} | |
template <typename Compare, typename Container> | |
void | |
check_compare_for_container () | |
{ | |
typedef typename Container::value_type T; | |
internal::trigger_static_asserts<internal::binary_predicate_tag, Compare, T, | |
T> (); | |
} | |
template <typename BinaryPredicate, typename Container> | |
void | |
check_binary_predicate_for_container () | |
{ | |
typedef typename Container::value_type T; | |
internal::trigger_static_asserts<internal::binary_predicate_tag, | |
BinaryPredicate, T, T> (); | |
} | |
// PrepareContainer and BackInserter are overloaded | |
// to increase performance on std::vector and std::string | |
// by using std::vector<T>::reserve | |
// and std::back_inserter instead of std::back_inserter. | |
// In VC2015, release mode, Celsius W520 Xeon | |
// this leads to an increase in performance of about a factor of 3 | |
// for transform. | |
template <typename C> | |
void | |
prepare_container ( | |
const std::basic_string<C, std::char_traits<C>, std::allocator<C> > &ys, | |
std::size_t size) | |
{ | |
ys.reserve (size); | |
} | |
template <typename Y> | |
void | |
prepare_container (std::vector<Y> &ys, std::size_t size) | |
{ | |
ys.reserve (size); | |
} | |
template <typename T, std::size_t N> | |
void | |
prepare_container (std::array<T, N> &, std::size_t size) | |
{ | |
assert (size == N); | |
unused (size); | |
} | |
template <typename Y> | |
void | |
prepare_container (std::unordered_set<Y> &ys, std::size_t size) | |
{ | |
ys.reserve (size); | |
} | |
template <typename Key, typename T> | |
void | |
prepare_container (std::unordered_map<Key, T> &ys, std::size_t size) | |
{ | |
ys.reserve (size); | |
} | |
template <typename Y> | |
void | |
prepare_container (std::unordered_multiset<Y> &ys, std::size_t size) | |
{ | |
ys.reserve (size); | |
} | |
template <typename Key, typename T> | |
void | |
prepare_container (std::unordered_multimap<Key, T> &ys, std::size_t size) | |
{ | |
ys.reserve (size); | |
} | |
template <typename Container> | |
void | |
prepare_container (Container &, std::size_t) | |
{ | |
} | |
template <typename Container> | |
std::back_insert_iterator<Container> | |
get_back_inserter (std::string &ys) | |
{ | |
return std::back_inserter (ys); | |
} | |
template <typename Container, typename Y> | |
std::back_insert_iterator<Container> | |
get_back_inserter (std::vector<Y> &ys) | |
{ | |
return std::back_inserter (ys); | |
} | |
template <typename Container, typename Y> | |
std::back_insert_iterator<Container> | |
get_back_inserter (std::list<Y> &ys) | |
{ | |
return std::back_inserter (ys); | |
} | |
template <typename Container, typename Y> | |
std::back_insert_iterator<Container> | |
get_back_inserter (std::deque<Y> &ys) | |
{ | |
return std::back_inserter (ys); | |
} | |
// Avoid self-assignment. | |
template <typename T> | |
void | |
assign (T &x, T &&y) | |
{ | |
if (&x != &y) | |
x = std::move (y); | |
} | |
template <typename T, std::size_t N> | |
struct array_back_insert_iterator | |
: public std::back_insert_iterator<std::array<T, N> > | |
{ | |
typedef std::back_insert_iterator<std::array<T, N> > base_type; | |
explicit array_back_insert_iterator (std::array<T, N> &arr) | |
: base_type (arr), arr_ptr_ (&arr), pos_ (0) | |
{ | |
} | |
array_back_insert_iterator (const array_back_insert_iterator<T, N> &other) | |
: base_type (*other.arr_ptr_), arr_ptr_ (other.arr_ptr_), | |
pos_ (other.pos_) | |
{ | |
} | |
array_back_insert_iterator<T, N> & | |
operator= (const array_back_insert_iterator<T, N> &other) | |
{ | |
arr_ptr_ = other.arr_ptr_; | |
pos_ = other.pos_; | |
return *this; | |
} | |
~array_back_insert_iterator () { assert (pos_ == 0 || pos_ == N); } | |
array_back_insert_iterator<T, N> & | |
operator= (const T &x) | |
{ | |
assert (pos_ < N); | |
(*arr_ptr_)[pos_] = x; | |
++pos_; | |
return *this; | |
} | |
array_back_insert_iterator<T, N> & | |
operator= (T &&x) | |
{ | |
assert (pos_ < N); | |
assign ((*arr_ptr_)[pos_], std::move (x)); | |
++pos_; | |
return *this; | |
} | |
array_back_insert_iterator<T, N> & | |
operator* () | |
{ | |
return *this; | |
} | |
array_back_insert_iterator<T, N> & | |
operator++ () | |
{ | |
return *this; | |
} | |
array_back_insert_iterator<T, N> | |
operator++ (int) | |
{ | |
return *this; | |
} | |
private: | |
std::array<T, N> *arr_ptr_; | |
std::size_t pos_; | |
}; | |
#if defined(_MSC_VER) && _MSC_VER >= 1900 | |
template <typename T, std::size_t N> | |
struct std::_Is_checked_helper<array_back_insert_iterator<T, N> > | |
: public true_type | |
{ // mark array_back_insert_iterator as checked | |
}; | |
#endif | |
template <typename Container, typename Y, std::size_t N> | |
array_back_insert_iterator<Y, N> | |
get_back_inserter (std::array<Y, N> &ys) | |
{ | |
return array_back_insert_iterator<Y, N> (ys); | |
} | |
template <typename Container> | |
std::insert_iterator<Container> | |
get_back_inserter (Container &ys) | |
{ | |
return std::inserter (ys, std::end (ys)); | |
} | |
template <typename Iterator> | |
void | |
advance_iterator (Iterator &it, std::size_t distance) | |
{ | |
std::advance (it, | |
static_cast<typename Iterator::difference_type> (distance)); | |
} | |
template <typename T> | |
void | |
advance_iterator (T *&it, std::size_t distance) | |
{ | |
it += static_cast<std::ptrdiff_t> (distance); | |
} | |
template <typename Iterator> | |
Iterator | |
add_to_iterator (Iterator it, std::size_t distance = 1) | |
{ | |
return std::next ( | |
it, static_cast<typename Iterator::difference_type> (distance)); | |
} | |
// GCC 4.9 does not support std::rbegin, std::rend and | |
// std::make_reverse_iterator | |
template <typename Iterator> | |
std::reverse_iterator<Iterator> | |
make_reverse_iterator (Iterator it) | |
{ | |
return std::reverse_iterator<Iterator> (it); | |
} | |
} // namespace internal | |
// API search type: is_even : Int -> Bool | |
// fwd bind count: 0 | |
// Checks if x is even. | |
template <typename X> | |
bool | |
is_even (X x) | |
{ | |
static_assert (std::is_integral<X>::value, "type must be integral"); | |
return x % 2 == 0; | |
} | |
// API search type: is_odd : Int -> Bool | |
// fwd bind count: 0 | |
// Checks if x is odd. | |
template <typename X> | |
bool | |
is_odd (X x) | |
{ | |
static_assert (std::is_integral<X>::value, "type must be integral"); | |
return x % 2 != 0; | |
} | |
// API search type: is_empty : [a] -> Bool | |
// fwd bind count: 0 | |
// Returns true if the container holds no elements. | |
// is_empty([1, 2]) == false | |
// is_empty([]) == true | |
template <typename Container> | |
bool | |
is_empty (const Container &xs) | |
{ | |
return xs.empty (); | |
} | |
// API search type: is_not_empty : [a] -> Bool | |
// fwd bind count: 0 | |
// Returns true if the container holds at least one element. | |
// is_not_empty([1, 2]) == true | |
template <typename Container> | |
bool | |
is_not_empty (const Container &xs) | |
{ | |
return !is_empty (xs); | |
} | |
// API search type: size_of_cont : [a] -> Int | |
// fwd bind count: 0 | |
// Returns the number of elements in the given container. | |
// size_of_cont([3, 4]) == 2 | |
template <typename Container> | |
std::size_t | |
size_of_cont (const Container &xs) | |
{ | |
return xs.size (); | |
} | |
// API search type: convert : a -> b | |
// fwd bind count: 0 | |
// Converts one type of element into another. | |
template <typename Dest, typename Source> | |
Dest | |
convert (const Source &x) | |
{ | |
return Dest (x); | |
} | |
// API search type: convert_elems : [a] -> [b] | |
// fwd bind count: 0 | |
// Converts all elements in a sequence to a different type. | |
// convert_elems<NewT>([1, 2, 3]) == [NewT(1), NewT(2), NewT(3)] | |
template <typename NewT, typename ContainerIn, | |
typename ContainerOut | |
= typename internal::same_cont_new_t<ContainerIn, NewT, 0>::type> | |
ContainerOut | |
convert_elems (const ContainerIn &xs) | |
{ | |
static_assert ( | |
std::is_constructible<NewT, typename ContainerIn::value_type>::value, | |
"Elements not convertible."); | |
ContainerOut ys; | |
internal::prepare_container (ys, size_of_cont (xs)); | |
auto it = internal::get_back_inserter<ContainerOut> (ys); | |
// using 'for (const auto& x ...)' is even for ints as fast as | |
// using 'for (int x ...)' (GCC, O3), so there is no need to | |
// check if the type is fundamental and then dispatch accordingly. | |
for (const auto &x : xs) | |
{ | |
*it = convert<NewT> (x); | |
} | |
return ys; | |
} | |
// API search type: convert_container : [a] -> [a] | |
// fwd bind count: 0 | |
// Change the type of the container | |
// while keeping the elements in the sequence the same. | |
// convert_container([1, 2, 3]) == [1, 2, 3] | |
// Useful for example if you want to convert an std::list to an std::vector. | |
template <typename ContainerOut, typename ContainerIn> | |
ContainerOut | |
convert_container (const ContainerIn &xs) | |
{ | |
typedef typename ContainerIn::value_type SourceElem; | |
typedef typename ContainerOut::value_type DestElem; | |
static_assert (std::is_same<DestElem, SourceElem>::value, | |
"Source and dest container must have the same value_type"); | |
ContainerOut ys; | |
internal::prepare_container (ys, size_of_cont (xs)); | |
auto itOut = internal::get_back_inserter<ContainerOut> (ys); | |
std::copy (std::begin (xs), std::end (xs), itOut); | |
return ys; | |
} | |
// API search type: convert_container_and_elems : [a] -> [b] | |
// fwd bind count: 0 | |
// Converts between different containers and elements. | |
// Dest elements are allowed to have explicit constructors. | |
// convert([1, 2, 3]) == [1, 2, 3] | |
template <typename ContainerOut, typename ContainerIn> | |
ContainerOut | |
convert_container_and_elems (const ContainerIn &xs) | |
{ | |
static_assert (std::is_convertible<typename ContainerIn::value_type, | |
typename ContainerOut::value_type>::value, | |
"Elements not convertible."); | |
typedef typename ContainerOut::value_type DestElem; | |
ContainerOut ys; | |
internal::prepare_container (ys, size_of_cont (xs)); | |
auto it = internal::get_back_inserter<ContainerOut> (ys); | |
for (const auto &x : xs) | |
{ | |
*it = convert<DestElem> (x); | |
} | |
return ys; | |
} | |
namespace internal | |
{ | |
template <typename Container> | |
Container | |
get_segment (internal::reuse_container_t, std::size_t idx_begin, | |
std::size_t idx_end, Container &&xs) | |
{ | |
idx_end = std::min (idx_end, size_of_cont (xs)); | |
if (idx_end <= idx_begin) | |
{ | |
xs.clear (); | |
return std::forward<Container> (xs); | |
} | |
auto itBegin = std::begin (xs); | |
internal::advance_iterator (itBegin, idx_begin); | |
auto itEnd = itBegin; | |
internal::advance_iterator (itEnd, idx_end - idx_begin); | |
xs.erase (std::copy (itBegin, itEnd, std::begin (xs)), std::end (xs)); | |
return std::forward<Container> (xs); | |
} | |
template <typename Container> | |
Container | |
get_segment (internal::create_new_container_t, std::size_t idx_begin, | |
std::size_t idx_end, const Container &xs) | |
{ | |
idx_end = std::min (idx_end, size_of_cont (xs)); | |
if (idx_end <= idx_begin) | |
{ | |
return {}; | |
} | |
Container result; | |
auto itBegin = std::begin (xs); | |
internal::advance_iterator (itBegin, idx_begin); | |
auto itEnd = itBegin; | |
internal::advance_iterator (itEnd, idx_end - idx_begin); | |
std::copy (itBegin, itEnd, internal::get_back_inserter (result)); | |
return result; | |
} | |
} // namespace internal | |
// API search type: get_segment : (Int, Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Return a defined segment from the sequence. | |
// get_segment(2, 5, [0,1,2,3,4,5,6,7,8]) == [2,3,4] | |
// get_segment(2, 15, [0,1,2,3,4,5,6,7,8]) == [2,3,4,5,6,7,8] | |
// get_segment(5, 2, [0,1,2,3,4,5,6,7,8]) == [] | |
// Also known as slice. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container> > | |
ContainerOut | |
get_segment (std::size_t idx_begin, std::size_t idx_end, Container &&xs) | |
{ | |
return internal::get_segment (internal::can_reuse_v<Container>{}, idx_begin, | |
idx_end, std::forward<Container> (xs)); | |
} | |
namespace internal | |
{ | |
template <typename ContainerToken, typename Container> | |
Container | |
set_segment (internal::reuse_container_t, std::size_t idx_begin, | |
const ContainerToken &token, Container &&xs) | |
{ | |
assert (idx_begin + size_of_cont (token) < size_of_cont (xs)); | |
auto itBegin = std::begin (xs); | |
internal::advance_iterator (itBegin, idx_begin); | |
std::copy (std::begin (token), std::end (token), itBegin); | |
return std::forward<Container> (xs); | |
} | |
template <typename ContainerToken, typename Container> | |
Container | |
set_segment (internal::create_new_container_t, std::size_t idx_begin, | |
const ContainerToken &token, const Container &xs) | |
{ | |
Container result = xs; | |
return set_segment (internal::reuse_container_t (), idx_begin, token, | |
std::move (result)); | |
} | |
} // namespace internal | |
// API search type: set_segment : (Int, [a], [a]) -> [a] | |
// fwd bind count: 2 | |
// Replace part of a sequence with a token. | |
// set_segment(2, [9,9,9], [0,1,2,3,4,5,6,7,8]) == [0,1,9,9,9,5,6,7,8] | |
// Crashes on invalid indices. | |
// Also known as replace_segment. | |
template <typename ContainerToken, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container> > | |
ContainerOut | |
set_segment (std::size_t idx_begin, const ContainerToken &token, | |
Container &&xs) | |
{ | |
return internal::set_segment (internal::can_reuse_v<Container>{}, idx_begin, | |
token, std::forward<Container> (xs)); | |
} | |
namespace internal | |
{ | |
template <typename Container> | |
Container | |
remove_segment (internal::reuse_container_t, std::size_t idx_begin, | |
std::size_t idx_end, Container &&xs) | |
{ | |
assert (idx_begin <= idx_end); | |
assert (idx_end <= size_of_cont (xs)); | |
auto firstBreakIt = std::begin (xs); | |
internal::advance_iterator (firstBreakIt, idx_begin); | |
auto secondBreakIt = std::begin (xs); | |
internal::advance_iterator (secondBreakIt, idx_end); | |
xs.erase (std::copy (secondBreakIt, std::end (xs), firstBreakIt), | |
std::end (xs)); | |
return std::forward<Container> (xs); | |
} | |
template <typename Container> | |
Container | |
remove_segment (internal::create_new_container_t, std::size_t idx_begin, | |
std::size_t idx_end, const Container &xs) | |
{ | |
assert (idx_begin <= idx_end); | |
assert (idx_end <= size_of_cont (xs)); | |
Container result; | |
std::size_t length = idx_end - idx_begin; | |
internal::prepare_container (result, size_of_cont (xs) - length); | |
auto firstBreakIt = std::begin (xs); | |
internal::advance_iterator (firstBreakIt, idx_begin); | |
std::copy (std::begin (xs), firstBreakIt, | |
internal::get_back_inserter (result)); | |
auto secondBreakIt = std::begin (xs); | |
internal::advance_iterator (secondBreakIt, idx_end); | |
std::copy (secondBreakIt, std::end (xs), | |
internal::get_back_inserter (result)); | |
return result; | |
} | |
} // namespace internal | |
// API search type: remove_segment : (Int, Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Cuts our a defined segment from the sequence. | |
// remove_segment(2, 5, [0,1,2,3,4,5,6,7]) == [0,1,5,6,7] | |
// crashes on invalid indices | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container> > | |
ContainerOut | |
remove_segment (std::size_t idx_begin, std::size_t idx_end, Container &&xs) | |
{ | |
return internal::remove_segment (internal::can_reuse_v<Container>{}, | |
idx_begin, idx_end, | |
std::forward<Container> (xs)); | |
} | |
// API search type: insert_at : (Int, [a], [a]) -> [a] | |
// fwd bind count: 2 | |
// Inserts a token into a sequence at a specific position. | |
// insert_at(2, [8,9], [0,1,2,3,4]) == [0,1,8,9,2,3,4] | |
// Unsafe! Crashes on invalid index. | |
template <typename Container> | |
Container | |
insert_at (std::size_t idx_begin, const Container &token, const Container &xs) | |
{ | |
assert (idx_begin <= size_of_cont (xs)); | |
Container result; | |
internal::prepare_container (result, | |
size_of_cont (xs) + size_of_cont (token)); | |
auto breakIt = std::begin (xs); | |
internal::advance_iterator (breakIt, idx_begin); | |
std::copy (std::begin (xs), breakIt, internal::get_back_inserter (result)); | |
std::copy (std::begin (token), std::end (token), | |
internal::get_back_inserter (result)); | |
std::copy (breakIt, std::end (xs), internal::get_back_inserter (result)); | |
return result; | |
} | |
// API search type: elem_at_idx : (Int, [a]) -> a | |
// fwd bind count: 1 | |
// Return the nth element of a sequence. | |
// elem_at_idx(2, [7,6,5,4,3]) == 5 | |
// Unsafe! Crashes on invalid index. | |
template <typename Container> | |
auto | |
elem_at_idx (std::size_t idx, const Container &xs) | |
{ | |
assert (idx < size_of_cont (xs)); | |
auto it = std::begin (xs); | |
internal::advance_iterator (it, idx); | |
return *it; | |
} | |
// API search type: elem_at_idx_maybe : (Int, [a]) -> Maybe a | |
// fwd bind count: 1 | |
// Return the nth element of a sequence if existing. | |
// elem_at_idx_maybe(2, [7,6,5,4,3]) == Just 5 | |
// elem_at_idx_maybe(9, [7,6,5,4,3]) == Nothing | |
// Use elem_at_idx_or_nothing if you want to provide a signed index type. | |
template <typename Container, typename T = typename Container::value_type> | |
maybe<T> | |
elem_at_idx_maybe (std::size_t idx, const Container &xs) | |
{ | |
if (size_of_cont (xs) < idx) | |
{ | |
return {}; | |
} | |
auto it = std::begin (xs); | |
internal::advance_iterator (it, idx); | |
return *it; | |
} | |
// API search type: elems_at_idxs : ([Int], [a]) -> [a] | |
// fwd bind count: 1 | |
// Construct a subsequence from the elements with the given indices. | |
// elem_at_idxs([1, 3], [7,6,5,4,3]) == [6, 4] | |
template <typename Container, typename ContainerIdxs, | |
typename T = typename Container::value_type, | |
typename ContainerOut = std::vector<T> > | |
std::vector<T> | |
elems_at_idxs (const ContainerIdxs &idxs, const Container &xs) | |
{ | |
static_assert ( | |
std::is_same<typename ContainerIdxs::value_type, std::size_t>::value, | |
"Indices must be std::size_t"); | |
ContainerOut result; | |
internal::prepare_container (result, size_of_cont (idxs)); | |
auto itOut = internal::get_back_inserter (result); | |
for (std::size_t idx : idxs) | |
{ | |
*itOut = elem_at_idx (idx, xs); | |
} | |
return result; | |
} | |
namespace internal | |
{ | |
template <typename Container, typename F> | |
Container | |
transform (internal::reuse_container_t, F f, Container &&xs) | |
{ | |
internal::trigger_static_asserts<internal::unary_function_tag, F, | |
decltype (*std::begin (xs))> (); | |
std::transform (std::begin (xs), std::end (xs), std::begin (xs), f); | |
return std::forward<Container> (xs); | |
} | |
template <typename ContainerOut, typename F, typename ContainerIn> | |
ContainerOut | |
transform (internal::create_new_container_t, F f, const ContainerIn &xs) | |
{ | |
internal::trigger_static_asserts<internal::unary_function_tag, F, | |
decltype (*std::begin (xs))> (); | |
ContainerOut ys; | |
internal::prepare_container (ys, size_of_cont (xs)); | |
auto it = internal::get_back_inserter<ContainerOut> (ys); | |
std::transform (std::begin (xs), std::end (xs), it, f); | |
return ys; | |
} | |
} // namespace internal | |
// API search type: transform : ((a -> b), [a]) -> [b] | |
// fwd bind count: 1 | |
// Apply a function to every element in a sequence. | |
// transform((*2), [1, 3, 4]) == [2, 6, 8] | |
// Also known as map or fmap. | |
template <typename F, typename ContainerIn, | |
typename ContainerOut | |
= typename internal::same_cont_new_t_from_unary_f< | |
internal::remove_const_and_ref_t<ContainerIn>, F, 0>::type> | |
ContainerOut | |
transform (F f, ContainerIn &&xs) | |
{ | |
using reuse_t = typename std::conditional< | |
std::is_same<internal::can_reuse_v<ContainerIn>, | |
internal::reuse_container_t>::value | |
&& std::is_base_of<std::true_type, | |
internal::has_order<ContainerIn> >::value | |
&& std::is_same<internal::remove_const_and_ref_t<ContainerIn>, | |
ContainerOut>::value, | |
internal::reuse_container_t, internal::create_new_container_t>::type; | |
return internal::transform<ContainerOut> (reuse_t{}, f, | |
std::forward<ContainerIn> (xs)); | |
} | |
// API search type: transform_convert : ((a -> b), [a]) -> [b] | |
// fwd bind count: 1 | |
// transform_convert((*2), [1, 3, 4]) == [2, 6, 8] | |
// Same as transform, but makes it easy to | |
// use an output container type different from the input container type. | |
template <typename ContainerOut, typename F, typename ContainerIn> | |
ContainerOut | |
transform_convert (F f, const ContainerIn &xs) | |
{ | |
internal::trigger_static_asserts<internal::unary_function_tag, F, | |
typename ContainerIn::value_type> (); | |
ContainerOut ys; | |
internal::prepare_container (ys, size_of_cont (xs)); | |
auto it = internal::get_back_inserter<ContainerOut> (ys); | |
std::transform (std::begin (xs), std::end (xs), it, f); | |
return ys; | |
} | |
// API search type: transform_inner : ((a -> b), [[a]]) -> [[b]] | |
// fwd bind count: 1 | |
// Applies a function to the elements of the inner containers | |
// of a nested sequence. | |
// transform_inner((*2), [[1, 3, 4], [1, 2]]) == [[2, 6, 8], [2, 4]] | |
// Also known as transform_nested, map_nested or map_inner. | |
template <typename F, typename ContainerIn, | |
typename ContainerOut = typename internal::same_cont_new_t< | |
ContainerIn, | |
typename internal::same_cont_new_t_from_unary_f< | |
typename ContainerIn::value_type, F, 0>::type, | |
0>::type> | |
ContainerOut | |
transform_inner (F f, const ContainerIn &xs) | |
{ | |
internal::trigger_static_asserts< | |
internal::unary_function_tag, F, | |
typename ContainerIn::value_type::value_type> (); | |
return fplus::transform ( | |
fplus::bind_1st_of_2 ( | |
fplus::transform<F, const typename ContainerIn::value_type &>, f), | |
xs); | |
} | |
namespace internal | |
{ | |
template <typename Container> | |
Container | |
reverse (internal::reuse_container_t, Container &&xs) | |
{ | |
static_assert (internal::has_order<Container>::value, | |
"Reverse: Container has no order."); | |
std::reverse (std::begin (xs), std::end (xs)); | |
return std::forward<Container> (xs); | |
} | |
template <typename Container> | |
Container | |
reverse (internal::create_new_container_t, const Container &xs) | |
{ | |
static_assert (internal::has_order<Container>::value, | |
"Reverse: Container has no order."); | |
Container ys = xs; | |
std::reverse (std::begin (ys), std::end (ys)); | |
return ys; | |
} | |
} // namespace internal | |
// API search type: reverse : [a] -> [a] | |
// fwd bind count: 0 | |
// Reverse a sequence. | |
// reverse([0,4,2,6]) == [6,2,4,0] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container> > | |
ContainerOut | |
reverse (Container &&xs) | |
{ | |
return internal::reverse (internal::can_reuse_v<Container>{}, | |
std::forward<Container> (xs)); | |
} | |
// API search type: take : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Return the first n elements of a sequence xs. | |
// In case n >= length(xs), xs is returned. | |
// take(3, [0,1,2,3,4,5,6,7]) == [0,1,2] | |
// take(10, [0,1,2]) == [0,1,2] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container> > | |
ContainerOut | |
take (std::size_t amount, Container &&xs) | |
{ | |
if (amount >= size_of_cont (xs)) | |
return xs; | |
return get_segment (0, amount, std::forward<Container> (xs)); | |
} | |
// API search type: take_exact : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Return exactly the first n elements of a sequence xs. | |
// Unsafe! Crashes then sequence is too short. | |
// take_exact(3, [0,1,2,3,4,5,6,7]) == [0,1,2] | |
// take_exact(10, [0,1,2]) == crash | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container> > | |
ContainerOut | |
take_exact (std::size_t amount, Container &&xs) | |
{ | |
return get_segment (0, amount, std::forward<Container> (xs)); | |
} | |
// API search type: take_cyclic : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Takes n elements from a sequence considering it as cyclic. | |
// take_cyclic(5, [0,1,2,3]) == [0,1,2,3,0] | |
// take_cyclic(7, [0,1,2,3]) == [0,1,2,3,0,1,2] | |
// take_cyclic(7, [0,1]) == [0,1,0,1,0,1,0] | |
// take_cyclic(2, [0,1,2,3]) == [0,1] | |
// take_cyclic(3, [0]) == [0,0,0] | |
// take_cyclic(3, []) == crash! | |
// Also known as take_wrap. | |
// xs must be non-empty. | |
template <typename Container> | |
Container | |
take_cyclic (std::size_t amount, const Container &xs) | |
{ | |
assert (!xs.empty ()); | |
Container ys; | |
internal::prepare_container (ys, size_of_cont (xs)); | |
auto it_out = internal::get_back_inserter (ys); | |
auto it_in = std::begin (xs); | |
while (amount != 0) | |
{ | |
*it_out = *it_in; | |
--amount; | |
++it_in; | |
if (it_in == std::end (xs)) | |
{ | |
it_in = std::begin (xs); | |
} | |
} | |
return ys; | |
} | |
// API search type: drop : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Skip the first n elements of a sequence xs. | |
// If n > length(xs) an empty sequence is returned. | |
// drop(3, [0,1,2,3,4,5,6,7]) == [3,4,5,6,7] | |
// Also known as skip. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container> > | |
ContainerOut | |
drop (std::size_t amount, Container &&xs) | |
{ | |
if (amount >= size_of_cont (xs)) | |
return ContainerOut (); | |
return get_segment (amount, size_of_cont (xs), std::forward<Container> (xs)); | |
} | |
// API search type: take_last : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Return the last n elements of a sequence xs. | |
// In case n >= length(xs), xs is returned. | |
// take_last(3, [0,1,2,3,4,5,6,7]) == [5,6,7] | |
// take_last(10, [0,1,2]) == [0,1,2] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container> > | |
ContainerOut | |
take_last (std::size_t amount, Container &&xs) | |
{ | |
if (amount >= size_of_cont (xs)) | |
return xs; | |
return drop (size_of_cont (xs) - amount, std::forward<Container> (xs)); | |
} | |
// API search type: drop_last : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Skip the last n elements of a sequence xs. | |
// If n > length(xs) an empty sequence is returned. | |
// drop_last(3, [0,1,2,3,4,5,6,7]) == [0,1,2,3,4] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container> > | |
ContainerOut | |
drop_last (std::size_t amount, Container &&xs) | |
{ | |
if (amount >= size_of_cont (xs)) | |
return ContainerOut (); | |
return take (size_of_cont (xs) - amount, std::forward<Container> (xs)); | |
} | |
// API search type: drop_exact : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Skip exactly the first n elements of a sequence xs. | |
// Unsafe! Crashes when xs is too short. | |
// drop_exact(3, [0,1,2,3,4,5,6,7]) == [3,4,5,6,7] | |
// drop_exact(10, [0,1,2,3,4,5,6,7]) == crash | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container> > | |
ContainerOut | |
drop_exact (std::size_t amount, Container &&xs) | |
{ | |
return get_segment (amount, size_of_cont (xs), std::forward<Container> (xs)); | |
} | |
// API search type: take_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Take elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// take_while(is_even, [0,2,4,5,6,7,8]) == [0,2,4] | |
template <typename Container, typename UnaryPredicate> | |
Container | |
take_while (UnaryPredicate pred, const Container &xs) | |
{ | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container> (); | |
auto itFirst | |
= std::find_if (std::begin (xs), std::end (xs), logical_not (pred)); | |
if (itFirst == std::end (xs)) | |
return xs; | |
return Container (std::begin (xs), itFirst); | |
} | |
// API search type: take_last_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Take elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// take_last_while(is_even, [0,2,7,5,6,4,8]) == [6,4,8] | |
template <typename Container, typename UnaryPredicate> | |
Container | |
take_last_while (UnaryPredicate pred, const Container &xs) | |
{ | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container> (); | |
const auto r_begin = internal::make_reverse_iterator (std::end (xs)); | |
const auto r_end = internal::make_reverse_iterator (std::begin (xs)); | |
const auto itFirstReverse | |
= std::find_if (r_begin, r_end, logical_not (pred)); | |
if (itFirstReverse == r_begin) | |
return Container (); | |
if (itFirstReverse == r_end) | |
return xs; | |
return Container (itFirstReverse.base (), std::end (xs)); | |
} | |
// API search type: drop_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Remove elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// drop_while(is_even, [0,2,4,5,6,7,8]) == [5,6,7,8] | |
// Also known as trim_left_by. | |
template <typename Container, typename UnaryPredicate> | |
Container | |
drop_while (UnaryPredicate pred, const Container &xs) | |
{ | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container> (); | |
auto itFirstNot = std::find_if_not (std::begin (xs), std::end (xs), pred); | |
if (itFirstNot == std::end (xs)) | |
return Container (); | |
return Container (itFirstNot, std::end (xs)); | |
} | |
// API search type: drop_last_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Remove elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// drop_last_while(is_even, [0,2,7,5,6,4,8]) == [0,2,7,5] | |
template <typename Container, typename UnaryPredicate> | |
Container | |
drop_last_while (UnaryPredicate pred, const Container &xs) | |
{ | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container> (); | |
const auto r_begin = internal::make_reverse_iterator (std::end (xs)); | |
const auto r_end = internal::make_reverse_iterator (std::begin (xs)); | |
const auto itFirstNotReverse = std::find_if_not (r_begin, r_end, pred); | |
if (itFirstNotReverse == r_begin) | |
return xs; | |
if (itFirstNotReverse == r_end) | |
return Container (); | |
return Container (std::begin (xs), itFirstNotReverse.base ()); | |
} | |
// API search type: fold_left : (((a, b) -> a), a, [b]) -> a | |
// fwd bind count: 2 | |
// fold_left((+), 0, [1, 2, 3]) == ((0+1)+2)+3 == 6 | |
// Takes the second argument and the first item of the list | |
// and applies the function to them, | |
// then feeds the function with this result and the second argument and so on. | |
template <typename F, typename Container, typename Acc> | |
Acc | |
fold_left (F f, const Acc &init, const Container &xs) | |
{ | |
using std::begin; | |
using std::end; | |
return internal::accumulate (begin (xs), end (xs), init, f); | |
} | |
// API search type: reduce : (((a, a) -> a), a, [a]) -> a | |
// fwd bind count: 2 | |
// reduce((+), 0, [1, 2, 3]) == (0+1+2+3) == 6 | |
// Combines the initial value and all elements of the sequence | |
// using the given function. | |
// The set of f, init and value_type should form a monoid. | |
template <typename F, typename Container> | |
typename Container::value_type | |
reduce (F f, const typename Container::value_type &init, const Container &xs) | |
{ | |
return fold_left (f, init, xs); | |
} | |
// API search type: fold_left_1 : (((a, a) -> a), [a]) -> a | |
// fwd bind count: 1 | |
// fold_left_1((+), [1, 2, 3]) == (1+2)+3 == 6 | |
// Takes the first 2 items of the list and applies the function to them, | |
// then feeds the function with this result and the third argument and so on. | |
// xs must be non-empty. | |
template <typename F, typename Container> | |
auto | |
fold_left_1 (F f, const Container &xs) | |
{ | |
assert (!xs.empty ()); | |
using std::begin; | |
using std::end; | |
const auto it = begin (xs); | |
return internal::accumulate (std::next (it), end (xs), *it, f); | |
} | |
// API search type: reduce_1 : (((a, a) -> a), [a]) -> a | |
// fwd bind count: 1 | |
// reduce_1((+), [1, 2, 3]) == (1+2+3) == 6 | |
// Joins all elements of the sequence using the given function. | |
// The set of f and value_type should form a semigroup. | |
template <typename F, typename Container> | |
typename Container::value_type | |
reduce_1 (F f, const Container &xs) | |
{ | |
assert (!xs.empty ()); | |
return fold_left_1 (f, xs); | |
} | |
// API search type: fold_right : (((a, b) -> b), b) -> [a] -> b | |
// fwd bind count: 2 | |
// fold_right((+), 0, [1, 2, 3]) == 1+(2+(3+0)) == 6 | |
// Takes the second argument and the last item of the list | |
// and applies the function, | |
// then it takes the penultimate item from the end and the result, and so on. | |
template <typename F, typename Container, typename Acc> | |
Acc | |
fold_right (F f, const Acc &init, const Container &xs) | |
{ | |
return internal::accumulate (xs.rbegin (), xs.rend (), init, flip (f)); | |
} | |
// API search type: fold_right_1 : (((a, a) -> a), [a]) -> a | |
// fwd bind count: 1 | |
// fold_right_1((+), [1, 2, 3]) == 1+(2+3)) == 6 | |
// Takes the last two items of the list and applies the function, | |
// then it takes the third item from the end and the result, and so on. | |
template <typename F, typename Container> | |
auto | |
fold_right_1 (F f, const Container &xs) | |
{ | |
assert (!xs.empty ()); | |
const auto it = xs.rbegin (); | |
return internal::accumulate (std::next (it), xs.rend (), *it, flip (f)); | |
} | |
// API search type: scan_left : (((a, b) -> a), a, [b]) -> [a] | |
// fwd bind count: 2 | |
// scan_left((+), 0, [1, 2, 3]) == [0, 1, 3, 6] | |
// Takes the second argument and the first item of the list | |
// and applies the function to them, | |
// then feeds the function with this result and the second argument and so on. | |
// It returns the list of intermediate and final results. | |
template <typename F, typename ContainerIn, typename Acc> | |
auto | |
scan_left (F f, const Acc &init, const ContainerIn &xs) | |
{ | |
using ContainerOut = | |
typename internal::same_cont_new_t<ContainerIn, Acc, 1>::type; | |
ContainerOut result; | |
internal::prepare_container (result, size_of_cont (xs) + 1); | |
using std::begin; | |
using std::end; | |
internal::scan_impl (f, init, internal::get_back_inserter (result), | |
begin (xs), end (xs)); | |
return result; | |
} | |
// API search type: scan_left_1 : (((a, a) -> a), [a]) -> [a] | |
// fwd bind count: 1 | |
// scan_left_1((+), [1, 2, 3]) == [1, 3, 6] | |
// Takes the first 2 items of the list and applies the function to them, | |
// then feeds the function with this result and the third argument and so on. | |
// It returns the list of intermediate and final results. | |
// xs must be non-empty. | |
template <typename F, typename ContainerIn> | |
auto | |
scan_left_1 (F f, const ContainerIn &xs) | |
{ | |
assert (!xs.empty ()); | |
using std::begin; | |
using std::end; | |
const auto beginIt = begin (xs); | |
using ContainerOut = typename internal::same_cont_new_t< | |
ContainerIn, internal::uncvref_t<decltype (*beginIt)>, 0>::type; | |
ContainerOut result; | |
internal::prepare_container (result, size_of_cont (xs)); | |
internal::scan_impl (f, *beginIt, internal::get_back_inserter (result), | |
std::next (beginIt), end (xs)); | |
return result; | |
} | |
// API search type: scan_right : (((a, b) -> b), b, [a]) -> [b] | |
// fwd bind count: 2 | |
// scan_right((+), 0, [1, 2, 3]) == [6, 5, 3, 0] | |
// Takes the second argument and the last item of the list | |
// and applies the function, | |
// then it takes the penultimate item from the end and the result, and so on. | |
// It returns the list of intermediate and final results. | |
template <typename F, typename ContainerIn, | |
typename Acc | |
= typename utils::function_traits<F>::template arg<1>::type, | |
typename ContainerOut | |
= typename internal::same_cont_new_t<ContainerIn, Acc, 1>::type> | |
ContainerOut | |
scan_right (F f, const Acc &init, const ContainerIn &xs) | |
{ | |
return reverse (scan_left (flip (f), init, reverse (xs))); | |
} | |
// API search type: scan_right_1 : (((a, a) -> a), [a]) -> [a] | |
// fwd bind count: 1 | |
// scan_right_1((+), [1, 2, 3]) == [6, 5, 3] | |
// Takes the last two items of the list and applies the function, | |
// then it takes the third item from the end and the result, and so on. | |
// It returns the list of inntermediate and final results. | |
template <typename F, typename ContainerIn, | |
typename Acc = typename ContainerIn::value_type, | |
typename ContainerOut | |
= typename internal::same_cont_new_t<ContainerIn, Acc, 0>::type> | |
ContainerOut | |
scan_right_1 (F f, const ContainerIn &xs) | |
{ | |
return reverse (scan_left_1 (flip (f), reverse (xs))); | |
} | |
// API search type: sum : [a] -> a | |
// fwd bind count: 0 | |
// Adds up all values in a sequence. | |
// sum([0,3,1]) == 4 | |
// sum([]) == 0 | |
template <typename Container, typename T = typename Container::value_type> | |
T | |
sum (const Container &xs) | |
{ | |
T result = T (); | |
for (const auto &x : xs) | |
{ | |
result = result + x; | |
} | |
return result; | |
} | |
// API search type: product : [a] -> a | |
// fwd bind count: 0 | |
// Returns the product of all values in a sequence. | |
// product([3,1,2]) == 6 | |
// product([]) == 1 | |
template <typename Container, typename T = typename Container::value_type> | |
T | |
product (const Container &xs) | |
{ | |
T result{ 1 }; | |
for (const auto &x : xs) | |
{ | |
result = result * x; | |
} | |
return result; | |
} | |
namespace internal | |
{ | |
template <typename T, typename Container> | |
Container | |
append_elem (internal::reuse_container_t, const T &y, Container &&xs) | |
{ | |
*internal::get_back_inserter (xs) = y; | |
return std::forward<Container> (xs); | |
} | |
template <typename T, typename Container> | |
Container | |
append_elem (internal::create_new_container_t, const T &y, const Container &xs) | |
{ | |
Container result; | |
internal::prepare_container (result, size_of_cont (xs) + 1); | |
std::copy (std::begin (xs), std::end (xs), | |
internal::get_back_inserter (result)); | |
*internal::get_back_inserter (result) = y; | |
return result; | |
} | |
} // namespace internal | |
// API search type: append_elem : (a, [a]) -> [a] | |
// fwd bind count: 1 | |
// Extends a sequence with one element at the back. | |
// append_elem([1, 2], 3) == [1, 2, 3] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>, | |
typename T = typename ContainerOut::value_type> | |
ContainerOut | |
append_elem (const T &y, Container &&xs) | |
{ | |
return internal::append_elem (internal::can_reuse_v<Container>{}, y, | |
std::forward<Container> (xs)); | |
} | |
namespace internal | |
{ | |
template <typename T> | |
std::list<T> | |
prepend_elem (internal::reuse_container_t, const T &y, std::list<T> &&xs) | |
{ | |
xs.push_front (y); | |
return std::forward<std::list<T> > (xs); | |
} | |
template <typename T, typename Container> | |
Container | |
prepend_elem (internal::reuse_container_t, const T &y, Container &&xs) | |
{ | |
xs.resize (size_of_cont (xs) + 1); | |
std::copy (++xs.rbegin (), xs.rend (), xs.rbegin ()); | |
*std::begin (xs) = y; | |
return std::forward<Container> (xs); | |
} | |
template <typename T, typename Container> | |
Container | |
prepend_elem (internal::create_new_container_t, const T &y, | |
const Container &xs) | |
{ | |
Container result; | |
internal::prepare_container (result, size_of_cont (xs) + 1); | |
*internal::get_back_inserter (result) = y; | |
std::copy (std::begin (xs), std::end (xs), | |
internal::get_back_inserter (result)); | |
return result; | |
} | |
} // namespace internal | |
// API search type: prepend_elem : (a, [a]) -> [a] | |
// fwd bind count: 1 | |
// Extends a sequence with one element in the front. | |
// prepend_elem([2, 3], 1) == [1, 2, 3] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>, | |
typename T = typename ContainerOut::value_type> | |
ContainerOut | |
prepend_elem (const T &y, Container &&xs) | |
{ | |
return internal::prepend_elem (internal::can_reuse_v<Container>{}, y, | |
std::forward<Container> (xs)); | |
} | |
// API search type: append : ([a], [a]) -> [a] | |
// fwd bind count: 1 | |
// Concatenates two sequences. | |
// append([1, 2], [3, 4, 5]) == [1, 2, 3, 4, 5] | |
template <typename ContainerIn1, typename ContainerIn2 = ContainerIn1, | |
typename ContainerOut = ContainerIn1> | |
ContainerOut | |
append (const ContainerIn1 &xs, const ContainerIn2 &ys) | |
{ | |
ContainerOut result; | |
internal::prepare_container (result, size_of_cont (xs) + size_of_cont (ys)); | |
std::copy (std::begin (xs), std::end (xs), | |
internal::get_back_inserter (result)); | |
std::copy (std::begin (ys), std::end (ys), | |
internal::get_back_inserter (result)); | |
return result; | |
} | |
// API search type: append_convert : ([a], [a]) -> [a] | |
// fwd bind count: 1 | |
// Same as append, but makes it easier to | |
// use an output container type different from the input container type. | |
template <typename ContainerOut, typename ContainerIn1, | |
typename ContainerIn2 = ContainerIn1> | |
ContainerOut | |
append_convert (const ContainerIn1 &xs, const ContainerIn2 &ys) | |
{ | |
return append<ContainerIn1, ContainerIn2, ContainerOut> (xs, ys); | |
} | |
// API search type: concat : [[a]] -> [a] | |
// fwd bind count: 0 | |
// Concatenates multiple sequences. | |
// concat([[1, 2], [], [3]]) == [1, 2, 3] | |
// Also known as flatten. | |
template <typename ContainerIn, | |
typename ContainerOut = typename ContainerIn::value_type> | |
ContainerOut | |
concat (const ContainerIn &xss) | |
{ | |
std::size_t length | |
= sum (transform (size_of_cont<typename ContainerIn::value_type>, xss)); | |
ContainerOut result; | |
internal::prepare_container (result, length); | |
using std::begin; | |
using std::end; | |
for (const auto &xs : xss) | |
{ | |
result.insert (end (result), begin (xs), end (xs)); | |
} | |
return result; | |
} | |
// API search type: interweave : ([a], [a]) -> [a] | |
// fwd bind count: 1 | |
// Return a sequence that contains elements from the two provided sequences | |
// in alternating order. If one list runs out of items, | |
// appends the items from the remaining list. | |
// interweave([1,3], [2,4]) == [1,2,3,4] | |
// interweave([1,3,5,7], [2,4]) == [1,2,3,4,5,7] | |
// See interleave for interweaving more than two sequences. | |
template <typename Container> | |
Container | |
interweave (const Container &xs, const Container &ys) | |
{ | |
Container result; | |
internal::prepare_container (result, size_of_cont (xs) + size_of_cont (ys)); | |
auto it = internal::get_back_inserter<Container> (result); | |
auto it_xs = std::begin (xs); | |
auto it_ys = std::begin (ys); | |
while (it_xs != std::end (xs) || it_ys != std::end (ys)) | |
{ | |
if (it_xs != std::end (xs)) | |
*it = *(it_xs++); | |
if (it_ys != std::end (ys)) | |
*it = *(it_ys++); | |
} | |
return result; | |
} | |
// API search type: unweave : [a] -> ([a], [a]) | |
// fwd bind count: 0 | |
// Puts the elements with an even index into the first list, | |
// and the elements with an odd index into the second list. | |
// Inverse of interweave. | |
// unweave([0,1,2,3]) == ([0,2], [1,3]) | |
// unweave([0,1,2,3,4]) == ([0,2,4], [1,3]) | |
template <typename Container> | |
std::pair<Container, Container> | |
unweave (const Container &xs) | |
{ | |
std::pair<Container, Container> result; | |
if (is_even (size_of_cont (xs))) | |
internal::prepare_container (result.first, size_of_cont (xs) / 2); | |
else | |
internal::prepare_container (result.first, size_of_cont (xs) / 2 + 1); | |
internal::prepare_container (result.second, size_of_cont (xs) / 2); | |
auto it_even = internal::get_back_inserter<Container> (result.first); | |
auto it_odd = internal::get_back_inserter<Container> (result.second); | |
std::size_t counter = 0; | |
for (const auto &x : xs) | |
{ | |
if (counter++ % 2 == 0) | |
*it_even = x; | |
else | |
*it_odd = x; | |
} | |
return result; | |
} | |
namespace internal | |
{ | |
template <typename Compare, typename T> | |
std::list<T> | |
sort_by (internal::reuse_container_t, Compare comp, std::list<T> &&xs) | |
{ | |
xs.sort (comp); | |
return std::forward<std::list<T> > (xs); | |
} | |
template <typename Compare, typename T> | |
std::list<T> | |
sort_by (internal::create_new_container_t, Compare comp, | |
const std::list<T> &xs) | |
{ | |
auto result = xs; | |
result.sort (comp); | |
return result; | |
} | |
template <typename Compare, typename Container> | |
Container | |
sort_by (internal::reuse_container_t, Compare comp, Container &&xs) | |
{ | |
std::sort (std::begin (xs), std::end (xs), comp); | |
return std::forward<Container> (xs); | |
} | |
template <typename Compare, typename Container> | |
Container | |
sort_by (internal::create_new_container_t, Compare comp, const Container &xs) | |
{ | |
auto result = xs; | |
std::sort (std::begin (result), std::end (result), comp); | |
return result; | |
} | |
} // namespace internal | |
// API search type: sort_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence by given less comparator. | |
template <typename Compare, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container> > | |
ContainerOut | |
sort_by (Compare comp, Container &&xs) | |
{ | |
return internal::sort_by (internal::can_reuse_v<Container>{}, comp, | |
std::forward<Container> (xs)); | |
} | |
namespace internal | |
{ | |
// workarounds for clang bug 24115 | |
// (std::sort and std::unique with std::function as comp) | |
// https://llvm.org/bugs/show_bug.cgi?id=24115 | |
template <typename F> struct is_less_by_struct | |
{ | |
is_less_by_struct (F f) : f_ (f){}; | |
template <typename T> | |
bool | |
operator() (const T &x, const T &y) | |
{ | |
return f_ (x) < f_ (y); | |
} | |
private: | |
F f_; | |
}; | |
template <typename F> struct is_equal_by_struct | |
{ | |
is_equal_by_struct (F f) : f_ (f){}; | |
template <typename T> | |
bool | |
operator() (const T &x, const T &y) | |
{ | |
return f_ (x) == f_ (y); | |
} | |
private: | |
F f_; | |
}; | |
} | |
// API search type: sort_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence by a given transformer. | |
template <typename F, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container> > | |
ContainerOut | |
sort_on (F f, Container &&xs) | |
{ | |
return sort_by (internal::is_less_by_struct<F> (f), | |
std::forward<Container> (xs)); | |
} | |
// API search type: sort : [a] -> [a] | |
// fwd bind count: 0 | |
// Sort a sequence to ascending order using std::less. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container> > | |
ContainerOut | |
sort (Container &&xs) | |
{ | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return sort_by (std::less<T> (), std::forward<Container> (xs)); | |
} | |
namespace internal | |
{ | |
template <typename Compare, typename T> | |
std::list<T> | |
stable_sort_by (internal::reuse_container_t, Compare comp, std::list<T> &&xs) | |
{ | |
xs.sort (comp); // std::list<T>::sort ist already stable. | |
return std::forward<std::list<T> > (xs); | |
} | |
template <typename Compare, typename T> | |
std::list<T> | |
stable_sort_by (internal::create_new_container_t, Compare comp, | |
const std::list<T> &xs) | |
{ | |
auto result = xs; | |
result.sort (comp); // std::list<T>::sort ist already stable. | |
return result; | |
} | |
template <typename Compare, typename Container> | |
Container | |
stable_sort_by (internal::reuse_container_t, Compare comp, Container &&xs) | |
{ | |
std::sort (std::begin (xs), std::end (xs), comp); | |
return std::forward<Container> (xs); | |
} | |
template <typename Compare, typename Container> | |
Container | |
stable_sort_by (internal::create_new_container_t, Compare comp, | |
const Container &xs) | |
{ | |
auto result = xs; | |
std::sort (std::begin (result), std::end (result), comp); | |
return result; | |
} | |
} // namespace internal | |
// API search type: stable_sort_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence stably by given less comparator. | |
template <typename Compare, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container> > | |
ContainerOut | |
stable_sort_by (Compare comp, Container &&xs) | |
{ | |
return internal::stable_sort_by (internal::can_reuse_v<Container>{}, comp, | |
std::forward<Container> (xs)); | |
} | |
// API search type: stable_sort_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence stably by given transformer. | |
template <typename F, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container> > | |
ContainerOut | |
stable_sort_on (F f, Container &&xs) | |
{ | |
return stable_sort_by (internal::is_less_by_struct<F> (f), | |
std::forward<Container> (xs)); | |
} | |
// API search type: stable_sort : [a] -> [a] | |
// fwd bind count: 0 | |
// Sort a sequence stably to ascending order using std::less. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container> > | |
ContainerOut | |
stable_sort (Container &&xs) | |
{ | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return stable_sort_by (std::less<T> (), std::forward<Container> (xs)); | |
} | |
namespace internal | |
{ | |
template <typename Compare, typename Container> | |
Container | |
partial_sort_by (internal::reuse_container_t, Compare comp, std::size_t count, | |
Container &&xs) | |
{ | |
if (count > xs.size ()) | |
{ | |
count = xs.size (); | |
} | |
auto middle = std::begin (xs); | |
internal::advance_iterator (middle, count); | |
std::partial_sort (std::begin (xs), middle, std::end (xs), comp); | |
return std::forward<Container> ( | |
get_segment (internal::reuse_container_t (), 0, count, xs)); | |
} | |
template <typename Compare, typename Container> | |
Container | |
partial_sort_by (internal::create_new_container_t, Compare comp, | |
std::size_t count, const Container &xs) | |
{ | |
auto result = xs; | |
return partial_sort_by (internal::reuse_container_t (), comp, count, | |
std::move (result)); | |
} | |
} // namespace internal | |
// API search type: partial_sort_by : (((a, a) -> Bool), Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Partially sort a sequence by a given less comparator. | |
// Returns only the sorted segment. | |
template <typename Compare, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container> > | |
ContainerOut | |
partial_sort_by (Compare comp, std::size_t count, Container &&xs) | |
{ | |
return internal::partial_sort_by (internal::can_reuse_v<Container>{}, comp, | |
count, std::forward<Container> (xs)); | |
} | |
// API search type: partial_sort_on : ((a -> b), Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Partially sort a sequence by a given transformer. | |
// Returns only the sorted segment. | |
template <typename F, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container> > | |
ContainerOut | |
partial_sort_on (F f, std::size_t count, Container &&xs) | |
{ | |
return partial_sort_by (internal::is_less_by_struct<F> (f), count, | |
std::forward<Container> (xs)); | |
} | |
// API search type: partial_sort : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Partially sort a sequence in ascending order using std::less. | |
// Returns only the sorted segment. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container> > | |
ContainerOut | |
partial_sort (std::size_t count, Container &&xs) | |
{ | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return partial_sort_by (std::less<T> (), count, | |
std::forward<Container> (xs)); | |
} | |
// API search type: nth_element_by : (((a, a) -> Bool), Int, [a]) -> a | |
// fwd bind count: 2 | |
// Return the nth largest element of a sequence by a given less comparator. | |
template <typename Compare, typename Container, | |
typename T = typename Container::value_type> | |
T | |
nth_element_by (Compare comp, std::size_t n, const Container &xs) | |
{ | |
assert (n < xs.size ()); | |
auto result = xs; | |
auto middle = std::begin (result); | |
internal::advance_iterator (middle, n); | |
std::nth_element (std::begin (result), middle, std::end (result), comp); | |
return *middle; | |
} | |
// API search type: nth_element_on : ((a -> b), Int, [a]) -> a | |
// fwd bind count: 2 | |
// Return the nth largest element of a sequence by a given transformer. | |
template <typename F, typename Container, | |
typename T = typename Container::value_type> | |
T | |
nth_element_on (F f, std::size_t n, const Container &xs) | |
{ | |
return nth_element_by (internal::is_less_by_struct<F> (f), n, xs); | |
} | |
// API search type: nth_element : (Int, [a]) -> a | |
// fwd bind count: 1 | |
// Return the nth largest element of a sequence using std::less. | |
template <typename Container, typename T = typename Container::value_type> | |
T | |
nth_element (std::size_t n, const Container &xs) | |
{ | |
return nth_element_by (std::less<T> (), n, xs); | |
} | |
namespace internal | |
{ | |
template <typename BinaryPredicate, typename Container> | |
Container | |
unique_by (internal::reuse_container_t, BinaryPredicate pred, Container &&xs) | |
{ | |
internal::check_binary_predicate_for_container<BinaryPredicate, | |
Container> (); | |
const auto it_end = std::unique (std::begin (xs), std::end (xs), pred); | |
xs.erase (it_end, std::end (xs)); | |
return std::forward<Container> (xs); | |
} | |
template <typename BinaryPredicate, typename Container> | |
Container | |
unique_by (internal::create_new_container_t, BinaryPredicate pred, | |
const Container &xs) | |
{ | |
auto result = xs; | |
return unique_by (internal::reuse_container_t (), pred, std::move (result)); | |
} | |
} // namespace internal | |
// API search type: unique_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Like unique, eliminates all but the first element | |
// from every consecutive group of equivalent elements from the sequence; | |
// but with a user supplied equality predicate. | |
// See nub_by for making elements globally unique in a sequence. | |
// O(n) | |
template <typename BinaryPredicate, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container> > | |
ContainerOut | |
unique_by (BinaryPredicate pred, Container &&xs) | |
{ | |
return internal::unique_by (internal::can_reuse_v<Container>{}, pred, | |
std::forward<Container> (xs)); | |
} | |
// API search type: unique_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Like unique, eliminates all but the first element | |
// from every consecutive group of equivalent elements from the sequence; | |
// but with a user supplied transformation (e.g. getter). | |
// See nub_on for making elements globally unique in a sequence. | |
// O(n) | |
// Also known as drop_repeats. | |
template <typename F, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container> > | |
ContainerOut | |
unique_on (F f, Container &&xs) | |
{ | |
return unique_by (internal::is_equal_by_struct<F> (f), | |
std::forward<Container> (xs)); | |
} | |
// API search type: unique : [a] -> [a] | |
// fwd bind count: 0 | |
// Eliminates all but the first element | |
// from every consecutive group of equivalent elements from the sequence. | |
// unique([1,2,2,3,2]) == [1,2,3,2] | |
// See nub for making elements globally unique in a sequence. | |
// O(n) | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container> > | |
ContainerOut | |
unique (Container &&xs) | |
{ | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return unique_on (identity<T>, std::forward<Container> (xs)); | |
} | |
// API search type: intersperse : (a, [a]) -> [a] | |
// fwd bind count: 1 | |
// Insert a value between all adjacent values in a sequence. | |
// intersperse(0, [1, 2, 3]) == [1, 0, 2, 0, 3] | |
// Also known as interpose. | |
template <typename Container, typename X = typename Container::value_type> | |
Container | |
intersperse (const X &value, const Container &xs) | |
{ | |
if (xs.empty ()) | |
return Container (); | |
if (size_of_cont (xs) == 1) | |
return xs; | |
Container result; | |
internal::prepare_container ( | |
result, std::max<std::size_t> (0, size_of_cont (xs) * 2 - 1)); | |
auto it = internal::get_back_inserter (result); | |
for_each (std::begin (xs), --std::end (xs), [&value, &it] (const X &x) { | |
*it = x; | |
*it = value; | |
}); | |
*it = xs.back (); | |
return result; | |
} | |
// API search type: join : ([a], [[a]]) -> [a] | |
// fwd bind count: 1 | |
// Inserts a separator sequence in between the elements | |
// of a sequence of sequences and concatenates the result. | |
// Also known as intercalate or implode. | |
// join(", ", ["a", "bee", "cee"]) == "a, bee, cee" | |
// join([0, 0], [[1], [2], [3, 4]]) == [1, 0, 0, 2, 0, 0, 3, 4] | |
template <typename Container, typename X = typename Container::value_type> | |
X | |
join (const X &separator, const Container &xs) | |
{ | |
return concat (intersperse (separator, xs)); | |
} | |
// API search type: join_elem : (a, [[a]]) -> [a] | |
// fwd bind count: 1 | |
// Inserts a separator in between the elements | |
// of a sequence of sequences and concatenates the result. | |
// Also known as intercalate_elem. | |
// join_elem(';', ["a", "bee", "cee"]) == "a;bee;cee" | |
// join_elem(0, [[1], [2], [3, 4]]) == [1, 0, 2, 0, 3, 4]] | |
template <typename Container, typename Inner = typename Container::value_type, | |
typename X = typename Inner::value_type> | |
Inner | |
join_elem (const X &separator, const Container &xs) | |
{ | |
return join (Inner (1, separator), xs); | |
} | |
// API search type: is_elem_of_by : ((a -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if at least one element of the container fulfils a predicate. | |
// is_elem_of_by((==), [1,2,3]) == true | |
template <typename UnaryPredicate, typename Container> | |
bool | |
is_elem_of_by (UnaryPredicate pred, const Container &xs) | |
{ | |
return std::find_if (std::begin (xs), std::end (xs), pred) != std::end (xs); | |
} | |
// API search type: is_elem_of : (a, [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if an element is a member of a container. | |
// is_elem_of(2, [1,2,3]) == true | |
// Also known as flip(contains). | |
template <typename Container> | |
bool | |
is_elem_of (const typename Container::value_type &x, const Container &xs) | |
{ | |
return is_elem_of_by (is_equal_to (x), xs); | |
} | |
// API search type: nub_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Makes the elements in a container unique with respect to a predicate | |
// nub_by((==), [1,2,2,3,2]) == [1,2,3] | |
// O(n^2) | |
template <typename Container, typename BinaryPredicate> | |
Container | |
nub_by (BinaryPredicate p, const Container &xs) | |
{ | |
Container result; | |
auto itOut = internal::get_back_inserter (result); | |
for (const auto &x : xs) | |
{ | |
auto eqToX = bind_1st_of_2 (p, x); | |
if (!is_elem_of_by (eqToX, result)) | |
{ | |
*itOut = x; | |
} | |
} | |
return result; | |
} | |
// API search type: nub_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Makes the elements in a container unique | |
// with respect to their function value. | |
// nub_on((mod 10), [12,32,15]) == [12,15] | |
// O(n^2) | |
template <typename Container, typename F> | |
Container | |
nub_on (F f, const Container &xs) | |
{ | |
return nub_by (is_equal_by (f), xs); | |
} | |
// API search type: nub : [a] -> [a] | |
// fwd bind count: 0 | |
// Makes the elements in a container unique. | |
// nub([1,2,2,3,2]) == [1,2,3] | |
// O(n^2) | |
// Also known as distinct. | |
template <typename Container> | |
Container | |
nub (const Container &xs) | |
{ | |
typedef typename Container::value_type T; | |
return nub_by (std::equal_to<T> (), xs); | |
} | |
// API search type: all_unique_by_eq : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all elements in a container are unique | |
// with respect to a predicate. | |
// Returns true for empty containers. | |
// O(n^2) | |
template <typename Container, typename BinaryPredicate> | |
bool | |
all_unique_by_eq (BinaryPredicate p, const Container &xs) | |
{ | |
internal::check_binary_predicate_for_container<BinaryPredicate, | |
Container> (); | |
return size_of_cont (nub_by (p, xs)) == size_of_cont (xs); | |
} | |
// API search type: all_unique_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all elements in a container are unique | |
// with respect to their function values. | |
// Returns true for empty containers. | |
// O(n^2) | |
template <typename Container, typename F> | |
bool | |
all_unique_on (F f, const Container &xs) | |
{ | |
return all_unique_by_eq (is_equal_by (f), xs); | |
} | |
// API search type: all_unique : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if all elements in a container are unique. | |
// Returns true for empty containers. | |
// O(n^2) | |
template <typename Container> | |
bool | |
all_unique (const Container &xs) | |
{ | |
typedef typename Container::value_type T; | |
auto comp = std::equal_to<T> (); | |
return all_unique_by_eq (comp, xs); | |
} | |
// API search type: is_strictly_sorted_by : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is strictly sorted using a predicate. | |
// comp(a, b) must return true only if a < b. | |
// O(n) | |
template <typename Container, typename Compare> | |
bool | |
is_strictly_sorted_by (Compare comp, const Container &xs) | |
{ | |
internal::check_compare_for_container<Compare, Container> (); | |
if (size_of_cont (xs) < 2) | |
return true; | |
auto it1 = std::begin (xs); | |
for (auto it2 = it1 + 1; it2 < std::end (xs); ++it1, ++it2) | |
if (!internal::invoke (comp, *it1, *it2)) | |
return false; | |
return true; | |
} | |
// API search type: is_strictly_sorted_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is strictly sorted using a transformer. | |
// O(n) | |
template <typename Container, typename F> | |
bool | |
is_strictly_sorted_on (F f, const Container &xs) | |
{ | |
return is_strictly_sorted_by (is_less_by (f), xs); | |
} | |
// API search type: is_strictly_sorted : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if a container already is strictly sorted | |
// in ascending order using std::less. | |
// O(n) | |
template <typename Container> | |
bool | |
is_strictly_sorted (const Container &xs) | |
{ | |
typedef typename Container::value_type T; | |
auto comp = std::less<T> (); | |
return is_strictly_sorted_by (comp, xs); | |
} | |
// API search type: is_sorted_by : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is sorted using a predicate. | |
// comp(a, b) must return true only if a < b. | |
// O(n) | |
template <typename Container, typename Compare> | |
bool | |
is_sorted_by (Compare comp, const Container &xs) | |
{ | |
internal::check_compare_for_container<Compare, Container> (); | |
if (size_of_cont (xs) < 2) | |
return true; | |
auto it1 = std::begin (xs); | |
for (auto it2 = it1 + 1; it2 < std::end (xs); ++it1, ++it2) | |
if (internal::invoke (comp, *it2, *it1)) | |
return false; | |
return true; | |
} | |
// API search type: is_sorted_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is strictly sorted using a transformer. | |
// O(n) | |
template <typename Container, typename F> | |
bool | |
is_sorted_on (F f, const Container &xs) | |
{ | |
return is_sorted_by (is_less_by (f), xs); | |
} | |
// API search type: is_sorted : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if a container already is sorted | |
// in ascending order using std::less. | |
// O(n) | |
template <typename Container> | |
bool | |
is_sorted (const Container &xs) | |
{ | |
typedef typename Container::value_type T; | |
auto comp = std::less<T> (); | |
return is_sorted_by (comp, xs); | |
} | |
// API search type: is_prefix_of : ([a], [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a containers starts with a token. | |
// is_prefix_of("Fun", "FunctionalPlus") == true | |
template <typename Container> | |
bool | |
is_prefix_of (const Container &token, const Container &xs) | |
{ | |
if (size_of_cont (token) > size_of_cont (xs)) | |
return false; | |
return get_segment (0, size_of_cont (token), xs) == token; | |
} | |
// API search type: is_suffix_of : ([a], [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a containers contains a token as a segment. | |
// is_suffix_of("us", "FunctionalPlus") == true | |
template <typename Container> | |
bool | |
is_suffix_of (const Container &token, const Container &xs) | |
{ | |
if (size_of_cont (token) > size_of_cont (xs)) | |
return false; | |
return get_segment (size_of_cont (xs) - size_of_cont (token), | |
size_of_cont (xs), xs) | |
== token; | |
} | |
// API search type: all_by : ((a -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a containers contains a token as a segment. | |
// all_by(is_even, [2, 4, 6]) == true | |
// Returns true for empty containers. | |
template <typename UnaryPredicate, typename Container> | |
bool | |
all_by (UnaryPredicate p, const Container &xs) | |
{ | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container> (); | |
return std::all_of (std::begin (xs), std::end (xs), p); | |
} | |
// API search type: all : [Bool] -> Bool | |
// fwd bind count: 0 | |
// Checks if all values in a container evaluate to true. | |
// all([true, false, true]) == false | |
// Returns true for empty containers. | |
template <typename Container> | |
bool | |
all (const Container &xs) | |
{ | |
typedef typename Container::value_type T; | |
return all_by (identity<T>, xs); | |
} | |
// API search type: all_the_same_by : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all values in a container are equal using a binary predicate. | |
// Returns true for empty containers. | |
template <typename Container, typename BinaryPredicate> | |
bool | |
all_the_same_by (BinaryPredicate p, const Container &xs) | |
{ | |
internal::check_binary_predicate_for_container<BinaryPredicate, | |
Container> (); | |
if (size_of_cont (xs) < 2) | |
return true; | |
auto unaryPredicate = bind_1st_of_2 (p, xs.front ()); | |
return all_by (unaryPredicate, xs); | |
} | |
// API search type: all_the_same_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all values in a container are equal using a transformer. | |
// Returns true for empty containers. | |
template <typename Container, typename F> | |
bool | |
all_the_same_on (F f, const Container &xs) | |
{ | |
if (size_of_cont (xs) < 2) | |
return true; | |
auto unaryPredicate = is_equal_by_to (f, f (xs.front ())); | |
return all_by (unaryPredicate, xs); | |
} | |
// API search type: all_the_same : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if all values in a container are equal. | |
// Returns true for empty containers. | |
template <typename Container> | |
bool | |
all_the_same (const Container &xs) | |
{ | |
typedef typename Container::value_type T; | |
auto binaryPredicate = std::equal_to<T> (); | |
return all_the_same_by (binaryPredicate, xs); | |
} | |
// API search type: numbers_step : (a, a, a) -> [a] | |
// fwd bind count: 2 | |
// Return a sequence of numbers using a specific step. | |
// numbers_step(2, 9, 2) == [2, 4, 6, 8] | |
template <typename T, typename ContainerOut = std::vector<T> > | |
ContainerOut | |
numbers_step (const T start, const T end, const T step) | |
{ | |
ContainerOut result; | |
if ((step > 0 && start >= end) || (step < 0 && start <= end) || step == 0) | |
{ | |
return result; | |
} | |
std::size_t size = static_cast<std::size_t> ((end - start) / step); | |
internal::prepare_container (result, size); | |
auto it = internal::get_back_inserter<ContainerOut> (result); | |
for (T x = start; x < end; x += step) | |
*it = x; | |
return result; | |
} | |
// API search type: numbers : (a, a) -> [a] | |
// fwd bind count: 1 | |
// Return an ascending sequence of numbers.. | |
// Also known as range. | |
// numbers(2, 9) == [2, 3, 4, 5, 6, 7, 8] | |
template <typename T, typename ContainerOut = std::vector<T> > | |
ContainerOut | |
numbers (const T start, const T end) | |
{ | |
return numbers_step<T, ContainerOut> (start, end, 1); | |
} | |
// API search type: singleton_seq : a -> [a] | |
// fwd bind count: 0 | |
// Construct a sequence containing a single value. | |
// singleton_seq(3) == [3]. | |
template <typename T, typename ContainerOut = std::vector<T> > | |
ContainerOut | |
singleton_seq (const T &x) | |
{ | |
return ContainerOut (1, x); | |
} | |
// API search type: all_idxs : [a] -> [Int] | |
// fwd bind count: 0 | |
// Returns a vector containing all valid indices of sequence xs. | |
// all_idxs([6,4,7,6]) == [0,1,2,3] | |
template <typename Container> | |
std::vector<std::size_t> | |
all_idxs (const Container &xs) | |
{ | |
return numbers<std::size_t> (0, size_of_cont (xs)); | |
} | |
// API search type: init : [a] -> [a] | |
// fwd bind count: 0 | |
// init([0,1,2,3]) == [0,1,2] | |
// Unsafe! xs must be non-empty. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container> > | |
ContainerOut | |
init (Container &&xs) | |
{ | |
assert (!is_empty (xs)); | |
return get_segment (0, size_of_cont (std::forward<Container> (xs)) - 1, xs); | |
} | |
// API search type: tail : [a] -> [a] | |
// fwd bind count: 0 | |
// Drops the first element of a container, keeps the rest. Unsafe! | |
// tail([0,1,2,3]) == [1,2,3] | |
// Unsafe! xs must be non-empty. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container> > | |
ContainerOut | |
tail (Container &&xs) | |
{ | |
assert (!is_empty (xs)); | |
return get_segment (1, size_of_cont (std::forward<Container> (xs)), xs); | |
} | |
// API search type: head : [a] -> a | |
// fwd bind count: 0 | |
// Return the first element of a container. | |
// head([0,1,2,3]) == 0 | |
// Unsafe! xs must be non-empty. | |
template <typename Container> | |
typename Container::value_type | |
head (const Container &xs) | |
{ | |
assert (!is_empty (xs)); | |
return xs.front (); | |
} | |
// API search type: last : [a] -> a | |
// fwd bind count: 0 | |
// Return the last element of a container. | |
// last([0,1,2,3]) == 3 | |
// Unsafe! xs must be non-empty. | |
template <typename Container> | |
typename Container::value_type | |
last (const Container &xs) | |
{ | |
assert (!is_empty (xs)); | |
return xs.back (); | |
} | |
// API search type: mean_stddev : [a] -> (a, a) | |
// fwd bind count: 0 | |
// Calculates the mean and the population standard deviation. | |
// mean_stddev([4, 8]) == (6, 2) | |
// mean_stddev([1, 3, 7, 4]) == (3.75, 2.5) | |
// xs must be non-empty. | |
template <typename Result, typename Container> | |
std::pair<Result, Result> | |
mean_stddev (const Container &xs) | |
{ | |
assert (size_of_cont (xs) != 0); | |
// http://stackoverflow.com/a/7616783/1866775 | |
Result sum = static_cast<Result> ( | |
internal::accumulate (xs.begin (), xs.end (), | |
static_cast<typename Container::value_type> (0))); | |
Result mean = sum / static_cast<Result> (xs.size ()); | |
std::vector<Result> diff (xs.size ()); | |
std::transform (xs.begin (), xs.end (), diff.begin (), | |
[mean] (Result x) { return x - mean; }); | |
Result sq_sum = std::inner_product (diff.begin (), diff.end (), | |
diff.begin (), static_cast<Result> (0)); | |
Result stddev = std::sqrt (sq_sum / static_cast<Result> (xs.size ())); | |
return std::make_pair (mean, stddev); | |
} | |
// API search type: count_occurrences_by : ((a -> b), [a]) -> Map b Int | |
// fwd bind count: 1 | |
// Returns a discrete frequency distribution of the elements in a container | |
// applying a specific transformer. | |
// count_occurrences_by(floor, [1.1, 2.3, 2.7, 3.6, 2.4]) == [(1, 1), (2, 3), | |
// (3, 1)] O(n) | |
template <typename F, typename ContainerIn> | |
auto | |
count_occurrences_by (F f, const ContainerIn &xs) | |
{ | |
using In = typename ContainerIn::value_type; | |
using MapOut | |
= std::map<std::decay_t<internal::invoke_result_t<F, In> >, std::size_t>; | |
internal::trigger_static_asserts<internal::unary_function_tag, F, | |
typename ContainerIn::value_type> (); | |
MapOut result; | |
for (const auto &x : xs) | |
{ | |
++result[internal::invoke (f, x)]; | |
} | |
return result; | |
} | |
// API search type: count_occurrences : [a] -> Map a Int | |
// fwd bind count: 0 | |
// Returns a discrete frequency distribution of the elements in a container | |
// applying a specific transformer. | |
// Can be used to create a histogram. | |
// count_occurrences([1,2,2,3,2]) == [(1, 1), (2, 3), (3, 1)] | |
// O(n) | |
template <typename ContainerIn, | |
typename MapOut | |
= typename std::map<typename ContainerIn::value_type, std::size_t> > | |
MapOut | |
count_occurrences (const ContainerIn &xs) | |
{ | |
return count_occurrences_by (identity<typename ContainerIn::value_type>, xs); | |
} | |
// API search type: lexicographical_less_by : (((a, a) -> Bool), [a], [a]) -> | |
// Bool fwd bind count: 2 Lexicographical less-than comparison using a specific | |
// predicate. lexicographical_less_by((<), [0,1,2,2,4,5], [0,1,2,3,4,5]) == | |
// true lexicographical_less_by((<), "012245", "012345") == true | |
// lexicographical_less_by((<), "01234", "012345") == true | |
// lexicographical_less_by((<), "012345", "01234") == false | |
// lexicographical_less_by((<), "012345", "012345") == false | |
template <typename Container, typename BinaryPredicate> | |
bool | |
lexicographical_less_by (BinaryPredicate p, const Container &xs, | |
const Container &ys) | |
{ | |
internal::check_binary_predicate_for_container<BinaryPredicate, | |
Container> (); | |
auto itXs = std::begin (xs); | |
auto itYs = std::begin (ys); | |
while (itXs != std::end (xs) && itYs != std::end (ys)) | |
{ | |
if (internal::invoke (p, *itXs, *itYs)) | |
{ | |
return true; | |
} | |
if (internal::invoke (p, *itYs, *itXs)) | |
{ | |
return false; | |
} | |
++itXs; | |
++itYs; | |
} | |
if (size_of_cont (xs) < size_of_cont (ys)) | |
{ | |
return true; | |
} | |
return false; | |
} | |
// API search type: lexicographical_less : ([a], [a]) -> Bool | |
// fwd bind count: 1 | |
// Lexicographical less-than comparison. | |
// lexicographical_less([0,1,2,2,4,5], [0,1,2,3,4,5]) == true | |
// lexicographical_less("012245", "012345") == true | |
// lexicographical_less("01234", "012345") == true | |
// lexicographical_less("012345", "01234") == false | |
// lexicographical_less("012345", "012345") == false | |
template <typename Container> | |
bool | |
lexicographical_less (const Container &xs, const Container &ys) | |
{ | |
return lexicographical_less_by (is_less<typename Container::value_type>, xs, | |
ys); | |
} | |
// API search type: lexicographical_sort : [[a]] -> [[a]] | |
// fwd bind count: 0 | |
// sort by lexicographical_less | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container> > | |
ContainerOut | |
lexicographical_sort (Container &&xs) | |
{ | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return sort_by (lexicographical_less<T>, std::forward<Container> (xs)); | |
} | |
// API search type: replicate : (Int, a) -> [a] | |
// fwd bind count: 1 | |
// Create a sequence containing x n times. | |
// replicate(3, 1) == [1, 1, 1] | |
template <typename T, typename ContainerOut = std::vector<T> > | |
ContainerOut | |
replicate (std::size_t n, const T &x) | |
{ | |
return ContainerOut (n, x); | |
} | |
namespace internal | |
{ | |
template <typename UnaryPredicate, typename T> | |
T | |
instead_of_if (internal::reuse_container_t, UnaryPredicate pred, const T &alt, | |
T &&x) | |
{ | |
if (internal::invoke (pred, x)) | |
return alt; | |
else | |
return std::forward<T> (x); | |
} | |
template <typename UnaryPredicate, typename T> | |
T | |
instead_of_if (internal::create_new_container_t, UnaryPredicate pred, | |
const T &alt, const T &x) | |
{ | |
if (internal::invoke (pred, x)) | |
return alt; | |
else | |
return x; | |
} | |
} // namespace internal | |
// API search type: instead_of_if : ((a -> Bool), a, a) -> a | |
// fwd bind count: 2 | |
// Return alt if pred(x), otherwise x itself. | |
template <typename UnaryPredicate, typename T, typename TAlt> | |
auto | |
instead_of_if (UnaryPredicate pred, const TAlt &alt, T &&x) | |
{ | |
return internal::instead_of_if (internal::can_reuse_v<T>{}, pred, alt, | |
std::forward<T> (x)); | |
} | |
// API search type: instead_of_if_empty : ((a -> Bool), [a], [a]) -> [a] | |
// fwd bind count: 2 | |
// Return alt if xs is empty, otherwise xs itself. | |
template <typename Container, typename ContainerAlt, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container> > | |
ContainerOut | |
instead_of_if_empty (const ContainerAlt &alt, Container &&xs) | |
{ | |
return instead_of_if (is_empty<internal::remove_const_and_ref_t<Container> >, | |
alt, std::forward<Container> (xs)); | |
} | |
} // namespace fplus |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. | |
// https://github.com/Dobiasd/FunctionalPlus | |
// Distributed under the Boost Software License, Version 1.0. | |
// (See accompanying file LICENSE_1_0.txt or copy at | |
// http://www.boost.org/LICENSE_1_0.txt) | |
#pragma once | |
#include <algorithm> | |
#include <cassert> | |
#include <cmath> | |
#include <cstddef> | |
#include <fplus/compare.hpp> | |
#include <fplus/composition.hpp> | |
#include <fplus/container_traits.hpp> | |
#include <fplus/internal/container_common.hpp> | |
#include <fplus/internal/invoke.hpp> | |
#include <fplus/internal/meta.hpp> | |
#include <fplus/maybe.hpp> | |
#include <iterator> | |
#include <numeric> | |
namespace fplus { | |
namespace internal { | |
template <typename UnaryPredicate, typename Container> | |
void check_unary_predicate_for_container() { | |
internal::check_unary_predicate_for_type<UnaryPredicate, | |
typename Container::value_type>(); | |
} | |
template <typename F, typename Container> | |
void check_index_with_type_predicate_for_container() { | |
typedef typename Container::value_type T; | |
internal::trigger_static_asserts<internal::binary_function_tag, F, | |
std::size_t, T>(); | |
static_assert( | |
std::is_convertible<internal::invoke_result_t<F, std::size_t, T>, | |
bool>::value, | |
"Function must return bool."); | |
} | |
template <typename Compare, typename Container> | |
void check_compare_for_container() { | |
typedef typename Container::value_type T; | |
internal::trigger_static_asserts<internal::binary_predicate_tag, Compare, T, | |
T>(); | |
} | |
template <typename BinaryPredicate, typename Container> | |
void check_binary_predicate_for_container() { | |
typedef typename Container::value_type T; | |
internal::trigger_static_asserts<internal::binary_predicate_tag, | |
BinaryPredicate, T, T>(); | |
} | |
// PrepareContainer and BackInserter are overloaded | |
// to increase performance on std::vector and std::string | |
// by using std::vector<T>::reserve | |
// and std::back_inserter instead of std::back_inserter. | |
// In VC2015, release mode, Celsius W520 Xeon | |
// this leads to an increase in performance of about a factor of 3 | |
// for transform. | |
template <typename C> | |
void prepare_container( | |
const std::basic_string<C, std::char_traits<C>, std::allocator<C>>& ys, | |
std::size_t size) { | |
ys.reserve(size); | |
} | |
template <typename Y> | |
void prepare_container(std::vector<Y>& ys, std::size_t size) { | |
ys.reserve(size); | |
} | |
template <typename T, std::size_t N> | |
void prepare_container(std::array<T, N>&, std::size_t size) { | |
assert(size == N); | |
unused(size); | |
} | |
template <typename Y> | |
void prepare_container(std::unordered_set<Y>& ys, std::size_t size) { | |
ys.reserve(size); | |
} | |
template <typename Key, typename T> | |
void prepare_container(std::unordered_map<Key, T>& ys, std::size_t size) { | |
ys.reserve(size); | |
} | |
template <typename Y> | |
void prepare_container(std::unordered_multiset<Y>& ys, std::size_t size) { | |
ys.reserve(size); | |
} | |
template <typename Key, typename T> | |
void prepare_container(std::unordered_multimap<Key, T>& ys, std::size_t size) { | |
ys.reserve(size); | |
} | |
template <typename Container> | |
void prepare_container(Container&, std::size_t) {} | |
template <typename Container> | |
std::back_insert_iterator<Container> get_back_inserter(std::string& ys) { | |
return std::back_inserter(ys); | |
} | |
template <typename Container, typename Y> | |
std::back_insert_iterator<Container> get_back_inserter(std::vector<Y>& ys) { | |
return std::back_inserter(ys); | |
} | |
template <typename Container, typename Y> | |
std::back_insert_iterator<Container> get_back_inserter(std::list<Y>& ys) { | |
return std::back_inserter(ys); | |
} | |
template <typename Container, typename Y> | |
std::back_insert_iterator<Container> get_back_inserter(std::deque<Y>& ys) { | |
return std::back_inserter(ys); | |
} | |
// Avoid self-assignment. | |
template <typename T> | |
void assign(T& x, T&& y) { | |
if (&x != &y) x = std::move(y); | |
} | |
template <typename T, std::size_t N> | |
struct array_back_insert_iterator | |
: public std::back_insert_iterator<std::array<T, N>> { | |
typedef std::back_insert_iterator<std::array<T, N>> base_type; | |
explicit array_back_insert_iterator(std::array<T, N>& arr) | |
: base_type(arr), arr_ptr_(&arr), pos_(0) {} | |
array_back_insert_iterator(const array_back_insert_iterator<T, N>& other) | |
: base_type(*other.arr_ptr_), | |
arr_ptr_(other.arr_ptr_), | |
pos_(other.pos_) {} | |
array_back_insert_iterator<T, N>& operator=( | |
const array_back_insert_iterator<T, N>& other) { | |
arr_ptr_ = other.arr_ptr_; | |
pos_ = other.pos_; | |
return *this; | |
} | |
~array_back_insert_iterator() { assert(pos_ == 0 || pos_ == N); } | |
array_back_insert_iterator<T, N>& operator=(const T& x) { | |
assert(pos_ < N); | |
(*arr_ptr_)[pos_] = x; | |
++pos_; | |
return *this; | |
} | |
array_back_insert_iterator<T, N>& operator=(T&& x) { | |
assert(pos_ < N); | |
assign((*arr_ptr_)[pos_], std::move(x)); | |
++pos_; | |
return *this; | |
} | |
array_back_insert_iterator<T, N>& operator*() { return *this; } | |
array_back_insert_iterator<T, N>& operator++() { return *this; } | |
array_back_insert_iterator<T, N> operator++(int) { return *this; } | |
private: | |
std::array<T, N>* arr_ptr_; | |
std::size_t pos_; | |
}; | |
#if defined(_MSC_VER) && _MSC_VER >= 1900 | |
template <typename T, std::size_t N> | |
struct std::_Is_checked_helper<array_back_insert_iterator<T, N>> | |
: public true_type { // mark array_back_insert_iterator as checked | |
}; | |
#endif | |
template <typename Container, typename Y, std::size_t N> | |
array_back_insert_iterator<Y, N> get_back_inserter(std::array<Y, N>& ys) { | |
return array_back_insert_iterator<Y, N>(ys); | |
} | |
template <typename Container> | |
std::insert_iterator<Container> get_back_inserter(Container& ys) { | |
return std::inserter(ys, std::end(ys)); | |
} | |
template <typename Iterator> | |
void advance_iterator(Iterator& it, std::size_t distance) { | |
std::advance(it, static_cast<typename Iterator::difference_type>(distance)); | |
} | |
template <typename T> | |
void advance_iterator(T*& it, std::size_t distance) { | |
it += static_cast<std::ptrdiff_t>(distance); | |
} | |
template <typename Iterator> | |
Iterator add_to_iterator(Iterator it, std::size_t distance = 1) { | |
return std::next(it, | |
static_cast<typename Iterator::difference_type>(distance)); | |
} | |
// GCC 4.9 does not support std::rbegin, std::rend and | |
// std::make_reverse_iterator | |
template <typename Iterator> | |
std::reverse_iterator<Iterator> make_reverse_iterator(Iterator it) { | |
return std::reverse_iterator<Iterator>(it); | |
} | |
} // namespace internal | |
// API search type: is_even : Int -> Bool | |
// fwd bind count: 0 | |
// Checks if x is even. | |
template <typename X> | |
bool is_even(X x) { | |
static_assert(std::is_integral<X>::value, "type must be integral"); | |
return x % 2 == 0; | |
} | |
// API search type: is_odd : Int -> Bool | |
// fwd bind count: 0 | |
// Checks if x is odd. | |
template <typename X> | |
bool is_odd(X x) { | |
static_assert(std::is_integral<X>::value, "type must be integral"); | |
return x % 2 != 0; | |
} | |
// API search type: is_empty : [a] -> Bool | |
// fwd bind count: 0 | |
// Returns true if the container holds no elements. | |
// is_empty([1, 2]) == false | |
// is_empty([]) == true | |
template <typename Container> | |
bool is_empty(const Container& xs) { | |
return xs.empty(); | |
} | |
// API search type: is_not_empty : [a] -> Bool | |
// fwd bind count: 0 | |
// Returns true if the container holds at least one element. | |
// is_not_empty([1, 2]) == true | |
template <typename Container> | |
bool is_not_empty(const Container& xs) { | |
return !is_empty(xs); | |
} | |
// API search type: size_of_cont : [a] -> Int | |
// fwd bind count: 0 | |
// Returns the number of elements in the given container. | |
// size_of_cont([3, 4]) == 2 | |
template <typename Container> | |
std::size_t size_of_cont(const Container& xs) { | |
return xs.size(); | |
} | |
// API search type: convert : a -> b | |
// fwd bind count: 0 | |
// Converts one type of element into another. | |
template <typename Dest, typename Source> | |
Dest convert(const Source& x) { | |
return Dest(x); | |
} | |
// API search type: convert_elems : [a] -> [b] | |
// fwd bind count: 0 | |
// Converts all elements in a sequence to a different type. | |
// convert_elems<NewT>([1, 2, 3]) == [NewT(1), NewT(2), NewT(3)] | |
template <typename NewT, typename ContainerIn, | |
typename ContainerOut = | |
typename internal::same_cont_new_t<ContainerIn, NewT, 0>::type> | |
ContainerOut convert_elems(const ContainerIn& xs) { | |
static_assert( | |
std::is_constructible<NewT, typename ContainerIn::value_type>::value, | |
"Elements not convertible."); | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it = internal::get_back_inserter<ContainerOut>(ys); | |
// using 'for (const auto& x ...)' is even for ints as fast as | |
// using 'for (int x ...)' (GCC, O3), so there is no need to | |
// check if the type is fundamental and then dispatch accordingly. | |
for (const auto& x : xs) { | |
*it = convert<NewT>(x); | |
} | |
return ys; | |
} | |
// API search type: convert_container : [a] -> [a] | |
// fwd bind count: 0 | |
// Change the type of the container | |
// while keeping the elements in the sequence the same. | |
// convert_container([1, 2, 3]) == [1, 2, 3] | |
// Useful for example if you want to convert an std::list to an std::vector. | |
template <typename ContainerOut, typename ContainerIn> | |
ContainerOut convert_container(const ContainerIn& xs) { | |
typedef typename ContainerIn::value_type SourceElem; | |
typedef typename ContainerOut::value_type DestElem; | |
static_assert(std::is_same<DestElem, SourceElem>::value, | |
"Source and dest container must have the same value_type"); | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto itOut = internal::get_back_inserter<ContainerOut>(ys); | |
std::copy(std::begin(xs), std::end(xs), itOut); | |
return ys; | |
} | |
// API search type: convert_container_and_elems : [a] -> [b] | |
// fwd bind count: 0 | |
// Converts between different containers and elements. | |
// Dest elements are allowed to have explicit constructors. | |
// convert([1, 2, 3]) == [1, 2, 3] | |
template <typename ContainerOut, typename ContainerIn> | |
ContainerOut convert_container_and_elems(const ContainerIn& xs) { | |
static_assert(std::is_convertible<typename ContainerIn::value_type, | |
typename ContainerOut::value_type>::value, | |
"Elements not convertible."); | |
typedef typename ContainerOut::value_type DestElem; | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it = internal::get_back_inserter<ContainerOut>(ys); | |
for (const auto& x : xs) { | |
*it = convert<DestElem>(x); | |
} | |
return ys; | |
} | |
namespace internal { | |
template <typename Container> | |
Container get_segment(internal::reuse_container_t, std::size_t idx_begin, | |
std::size_t idx_end, Container&& xs) { | |
idx_end = std::min(idx_end, size_of_cont(xs)); | |
if (idx_end <= idx_begin) { | |
xs.clear(); | |
return std::forward<Container>(xs); | |
} | |
auto itBegin = std::begin(xs); | |
internal::advance_iterator(itBegin, idx_begin); | |
auto itEnd = itBegin; | |
internal::advance_iterator(itEnd, idx_end - idx_begin); | |
xs.erase(std::copy(itBegin, itEnd, std::begin(xs)), std::end(xs)); | |
return std::forward<Container>(xs); | |
} | |
template <typename Container> | |
Container get_segment(internal::create_new_container_t, std::size_t idx_begin, | |
std::size_t idx_end, const Container& xs) { | |
idx_end = std::min(idx_end, size_of_cont(xs)); | |
if (idx_end <= idx_begin) { | |
return {}; | |
} | |
Container result; | |
auto itBegin = std::begin(xs); | |
internal::advance_iterator(itBegin, idx_begin); | |
auto itEnd = itBegin; | |
internal::advance_iterator(itEnd, idx_end - idx_begin); | |
std::copy(itBegin, itEnd, internal::get_back_inserter(result)); | |
return result; | |
} | |
} // namespace internal | |
// API search type: get_segment : (Int, Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Return a defined segment from the sequence. | |
// get_segment(2, 5, [0,1,2,3,4,5,6,7,8]) == [2,3,4] | |
// get_segment(2, 15, [0,1,2,3,4,5,6,7,8]) == [2,3,4,5,6,7,8] | |
// get_segment(5, 2, [0,1,2,3,4,5,6,7,8]) == [] | |
// Also known as slice. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut get_segment(std::size_t idx_begin, std::size_t idx_end, | |
Container&& xs) { | |
return internal::get_segment(internal::can_reuse_v<Container>{}, idx_begin, | |
idx_end, std::forward<Container>(xs)); | |
} | |
namespace internal { | |
template <typename ContainerToken, typename Container> | |
Container set_segment(internal::reuse_container_t, std::size_t idx_begin, | |
const ContainerToken& token, Container&& xs) { | |
assert(idx_begin + size_of_cont(token) < size_of_cont(xs)); | |
auto itBegin = std::begin(xs); | |
internal::advance_iterator(itBegin, idx_begin); | |
std::copy(std::begin(token), std::end(token), itBegin); | |
return std::forward<Container>(xs); | |
} | |
template <typename ContainerToken, typename Container> | |
Container set_segment(internal::create_new_container_t, std::size_t idx_begin, | |
const ContainerToken& token, const Container& xs) { | |
Container result = xs; | |
return set_segment(internal::reuse_container_t(), idx_begin, token, | |
std::move(result)); | |
} | |
} // namespace internal | |
// API search type: set_segment : (Int, [a], [a]) -> [a] | |
// fwd bind count: 2 | |
// Replace part of a sequence with a token. | |
// set_segment(2, [9,9,9], [0,1,2,3,4,5,6,7,8]) == [0,1,9,9,9,5,6,7,8] | |
// Crashes on invalid indices. | |
// Also known as replace_segment. | |
template <typename ContainerToken, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut set_segment(std::size_t idx_begin, const ContainerToken& token, | |
Container&& xs) { | |
return internal::set_segment(internal::can_reuse_v<Container>{}, idx_begin, | |
token, std::forward<Container>(xs)); | |
} | |
namespace internal { | |
template <typename Container> | |
Container remove_segment(internal::reuse_container_t, std::size_t idx_begin, | |
std::size_t idx_end, Container&& xs) { | |
assert(idx_begin <= idx_end); | |
assert(idx_end <= size_of_cont(xs)); | |
auto firstBreakIt = std::begin(xs); | |
internal::advance_iterator(firstBreakIt, idx_begin); | |
auto secondBreakIt = std::begin(xs); | |
internal::advance_iterator(secondBreakIt, idx_end); | |
xs.erase(std::copy(secondBreakIt, std::end(xs), firstBreakIt), std::end(xs)); | |
return std::forward<Container>(xs); | |
} | |
template <typename Container> | |
Container remove_segment(internal::create_new_container_t, | |
std::size_t idx_begin, std::size_t idx_end, | |
const Container& xs) { | |
assert(idx_begin <= idx_end); | |
assert(idx_end <= size_of_cont(xs)); | |
Container result; | |
std::size_t length = idx_end - idx_begin; | |
internal::prepare_container(result, size_of_cont(xs) - length); | |
auto firstBreakIt = std::begin(xs); | |
internal::advance_iterator(firstBreakIt, idx_begin); | |
std::copy(std::begin(xs), firstBreakIt, internal::get_back_inserter(result)); | |
auto secondBreakIt = std::begin(xs); | |
internal::advance_iterator(secondBreakIt, idx_end); | |
std::copy(secondBreakIt, std::end(xs), internal::get_back_inserter(result)); | |
return result; | |
} | |
} // namespace internal | |
// API search type: remove_segment : (Int, Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Cuts our a defined segment from the sequence. | |
// remove_segment(2, 5, [0,1,2,3,4,5,6,7]) == [0,1,5,6,7] | |
// crashes on invalid indices | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut remove_segment(std::size_t idx_begin, std::size_t idx_end, | |
Container&& xs) { | |
return internal::remove_segment(internal::can_reuse_v<Container>{}, idx_begin, | |
idx_end, std::forward<Container>(xs)); | |
} | |
// API search type: insert_at : (Int, [a], [a]) -> [a] | |
// fwd bind count: 2 | |
// Inserts a token into a sequence at a specific position. | |
// insert_at(2, [8,9], [0,1,2,3,4]) == [0,1,8,9,2,3,4] | |
// Unsafe! Crashes on invalid index. | |
template <typename Container> | |
Container insert_at(std::size_t idx_begin, const Container& token, | |
const Container& xs) { | |
assert(idx_begin <= size_of_cont(xs)); | |
Container result; | |
internal::prepare_container(result, size_of_cont(xs) + size_of_cont(token)); | |
auto breakIt = std::begin(xs); | |
internal::advance_iterator(breakIt, idx_begin); | |
std::copy(std::begin(xs), breakIt, internal::get_back_inserter(result)); | |
std::copy(std::begin(token), std::end(token), | |
internal::get_back_inserter(result)); | |
std::copy(breakIt, std::end(xs), internal::get_back_inserter(result)); | |
return result; | |
} | |
// API search type: elem_at_idx : (Int, [a]) -> a | |
// fwd bind count: 1 | |
// Return the nth element of a sequence. | |
// elem_at_idx(2, [7,6,5,4,3]) == 5 | |
// Unsafe! Crashes on invalid index. | |
template <typename Container> | |
auto elem_at_idx(std::size_t idx, const Container& xs) { | |
assert(idx < size_of_cont(xs)); | |
auto it = std::begin(xs); | |
internal::advance_iterator(it, idx); | |
return *it; | |
} | |
// API search type: elem_at_idx_maybe : (Int, [a]) -> Maybe a | |
// fwd bind count: 1 | |
// Return the nth element of a sequence if existing. | |
// elem_at_idx_maybe(2, [7,6,5,4,3]) == Just 5 | |
// elem_at_idx_maybe(9, [7,6,5,4,3]) == Nothing | |
// Use elem_at_idx_or_nothing if you want to provide a signed index type. | |
template <typename Container, typename T = typename Container::value_type> | |
maybe<T> elem_at_idx_maybe(std::size_t idx, const Container& xs) { | |
if (size_of_cont(xs) < idx) { | |
return {}; | |
} | |
auto it = std::begin(xs); | |
internal::advance_iterator(it, idx); | |
return *it; | |
} | |
// API search type: elems_at_idxs : ([Int], [a]) -> [a] | |
// fwd bind count: 1 | |
// Construct a subsequence from the elements with the given indices. | |
// elem_at_idxs([1, 3], [7,6,5,4,3]) == [6, 4] | |
template <typename Container, typename ContainerIdxs, | |
typename T = typename Container::value_type, | |
typename ContainerOut = std::vector<T>> | |
std::vector<T> elems_at_idxs(const ContainerIdxs& idxs, const Container& xs) { | |
static_assert( | |
std::is_same<typename ContainerIdxs::value_type, std::size_t>::value, | |
"Indices must be std::size_t"); | |
ContainerOut result; | |
internal::prepare_container(result, size_of_cont(idxs)); | |
auto itOut = internal::get_back_inserter(result); | |
for (std::size_t idx : idxs) { | |
*itOut = elem_at_idx(idx, xs); | |
} | |
return result; | |
} | |
namespace internal { | |
template <typename Container, typename F> | |
Container transform(internal::reuse_container_t, F f, Container&& xs) { | |
internal::trigger_static_asserts<internal::unary_function_tag, F, | |
decltype(*std::begin(xs))>(); | |
std::transform(std::begin(xs), std::end(xs), std::begin(xs), f); | |
return std::forward<Container>(xs); | |
} | |
template <typename ContainerOut, typename F, typename ContainerIn> | |
ContainerOut transform(internal::create_new_container_t, F f, | |
const ContainerIn& xs) { | |
internal::trigger_static_asserts<internal::unary_function_tag, F, | |
decltype(*std::begin(xs))>(); | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it = internal::get_back_inserter<ContainerOut>(ys); | |
std::transform(std::begin(xs), std::end(xs), it, f); | |
return ys; | |
} | |
} // namespace internal | |
// API search type: transform : ((a -> b), [a]) -> [b] | |
// fwd bind count: 1 | |
// Apply a function to every element in a sequence. | |
// transform((*2), [1, 3, 4]) == [2, 6, 8] | |
// Also known as map or fmap. | |
template < | |
typename F, typename ContainerIn, | |
typename ContainerOut = typename internal::same_cont_new_t_from_unary_f< | |
internal::remove_const_and_ref_t<ContainerIn>, F, 0>::type> | |
ContainerOut transform(F f, ContainerIn&& xs) { | |
using reuse_t = typename std::conditional< | |
std::is_same<internal::can_reuse_v<ContainerIn>, | |
internal::reuse_container_t>::value && | |
std::is_base_of<std::true_type, | |
internal::has_order<ContainerIn>>::value && | |
std::is_same<internal::remove_const_and_ref_t<ContainerIn>, | |
ContainerOut>::value, | |
internal::reuse_container_t, internal::create_new_container_t>::type; | |
return internal::transform<ContainerOut>(reuse_t{}, f, | |
std::forward<ContainerIn>(xs)); | |
} | |
// API search type: transform_convert : ((a -> b), [a]) -> [b] | |
// fwd bind count: 1 | |
// transform_convert((*2), [1, 3, 4]) == [2, 6, 8] | |
// Same as transform, but makes it easy to | |
// use an output container type different from the input container type. | |
template <typename ContainerOut, typename F, typename ContainerIn> | |
ContainerOut transform_convert(F f, const ContainerIn& xs) { | |
internal::trigger_static_asserts<internal::unary_function_tag, F, | |
typename ContainerIn::value_type>(); | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it = internal::get_back_inserter<ContainerOut>(ys); | |
std::transform(std::begin(xs), std::end(xs), it, f); | |
return ys; | |
} | |
// API search type: transform_inner : ((a -> b), [[a]]) -> [[b]] | |
// fwd bind count: 1 | |
// Applies a function to the elements of the inner containers | |
// of a nested sequence. | |
// transform_inner((*2), [[1, 3, 4], [1, 2]]) == [[2, 6, 8], [2, 4]] | |
// Also known as transform_nested, map_nested or map_inner. | |
template <typename F, typename ContainerIn, | |
typename ContainerOut = typename internal::same_cont_new_t< | |
ContainerIn, | |
typename internal::same_cont_new_t_from_unary_f< | |
typename ContainerIn::value_type, F, 0>::type, | |
0>::type> | |
ContainerOut transform_inner(F f, const ContainerIn& xs) { | |
internal::trigger_static_asserts< | |
internal::unary_function_tag, F, | |
typename ContainerIn::value_type::value_type>(); | |
return fplus::transform( | |
fplus::bind_1st_of_2( | |
fplus::transform<F, const typename ContainerIn::value_type&>, f), | |
xs); | |
} | |
namespace internal { | |
template <typename Container> | |
Container reverse(internal::reuse_container_t, Container&& xs) { | |
static_assert(internal::has_order<Container>::value, | |
"Reverse: Container has no order."); | |
std::reverse(std::begin(xs), std::end(xs)); | |
return std::forward<Container>(xs); | |
} | |
template <typename Container> | |
Container reverse(internal::create_new_container_t, const Container& xs) { | |
static_assert(internal::has_order<Container>::value, | |
"Reverse: Container has no order."); | |
Container ys = xs; | |
std::reverse(std::begin(ys), std::end(ys)); | |
return ys; | |
} | |
} // namespace internal | |
// API search type: reverse : [a] -> [a] | |
// fwd bind count: 0 | |
// Reverse a sequence. | |
// reverse([0,4,2,6]) == [6,2,4,0] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut reverse(Container&& xs) { | |
return internal::reverse(internal::can_reuse_v<Container>{}, | |
std::forward<Container>(xs)); | |
} | |
// API search type: take : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Return the first n elements of a sequence xs. | |
// In case n >= length(xs), xs is returned. | |
// take(3, [0,1,2,3,4,5,6,7]) == [0,1,2] | |
// take(10, [0,1,2]) == [0,1,2] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut take(std::size_t amount, Container&& xs) { | |
if (amount >= size_of_cont(xs)) return xs; | |
return get_segment(0, amount, std::forward<Container>(xs)); | |
} | |
// API search type: take_exact : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Return exactly the first n elements of a sequence xs. | |
// Unsafe! Crashes then sequence is too short. | |
// take_exact(3, [0,1,2,3,4,5,6,7]) == [0,1,2] | |
// take_exact(10, [0,1,2]) == crash | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut take_exact(std::size_t amount, Container&& xs) { | |
return get_segment(0, amount, std::forward<Container>(xs)); | |
} | |
// API search type: take_cyclic : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Takes n elements from a sequence considering it as cyclic. | |
// take_cyclic(5, [0,1,2,3]) == [0,1,2,3,0] | |
// take_cyclic(7, [0,1,2,3]) == [0,1,2,3,0,1,2] | |
// take_cyclic(7, [0,1]) == [0,1,0,1,0,1,0] | |
// take_cyclic(2, [0,1,2,3]) == [0,1] | |
// take_cyclic(3, [0]) == [0,0,0] | |
// take_cyclic(3, []) == crash! | |
// Also known as take_wrap. | |
// xs must be non-empty. | |
template <typename Container> | |
Container take_cyclic(std::size_t amount, const Container& xs) { | |
assert(!xs.empty()); | |
Container ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it_out = internal::get_back_inserter(ys); | |
auto it_in = std::begin(xs); | |
while (amount != 0) { | |
*it_out = *it_in; | |
--amount; | |
++it_in; | |
if (it_in == std::end(xs)) { | |
it_in = std::begin(xs); | |
} | |
} | |
return ys; | |
} | |
// API search type: drop : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Skip the first n elements of a sequence xs. | |
// If n > length(xs) an empty sequence is returned. | |
// drop(3, [0,1,2,3,4,5,6,7]) == [3,4,5,6,7] | |
// Also known as skip. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut drop(std::size_t amount, Container&& xs) { | |
if (amount >= size_of_cont(xs)) return ContainerOut(); | |
return get_segment(amount, size_of_cont(xs), std::forward<Container>(xs)); | |
} | |
// API search type: take_last : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Return the last n elements of a sequence xs. | |
// In case n >= length(xs), xs is returned. | |
// take_last(3, [0,1,2,3,4,5,6,7]) == [5,6,7] | |
// take_last(10, [0,1,2]) == [0,1,2] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut take_last(std::size_t amount, Container&& xs) { | |
if (amount >= size_of_cont(xs)) return xs; | |
return drop(size_of_cont(xs) - amount, std::forward<Container>(xs)); | |
} | |
// API search type: drop_last : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Skip the last n elements of a sequence xs. | |
// If n > length(xs) an empty sequence is returned. | |
// drop_last(3, [0,1,2,3,4,5,6,7]) == [0,1,2,3,4] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut drop_last(std::size_t amount, Container&& xs) { | |
if (amount >= size_of_cont(xs)) return ContainerOut(); | |
return take(size_of_cont(xs) - amount, std::forward<Container>(xs)); | |
} | |
// API search type: drop_exact : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Skip exactly the first n elements of a sequence xs. | |
// Unsafe! Crashes when xs is too short. | |
// drop_exact(3, [0,1,2,3,4,5,6,7]) == [3,4,5,6,7] | |
// drop_exact(10, [0,1,2,3,4,5,6,7]) == crash | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut drop_exact(std::size_t amount, Container&& xs) { | |
return get_segment(amount, size_of_cont(xs), std::forward<Container>(xs)); | |
} | |
// API search type: take_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Take elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// take_while(is_even, [0,2,4,5,6,7,8]) == [0,2,4] | |
template <typename Container, typename UnaryPredicate> | |
Container take_while(UnaryPredicate pred, const Container& xs) { | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
auto itFirst = std::find_if(std::begin(xs), std::end(xs), logical_not(pred)); | |
if (itFirst == std::end(xs)) return xs; | |
return Container(std::begin(xs), itFirst); | |
} | |
// API search type: take_last_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Take elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// take_last_while(is_even, [0,2,7,5,6,4,8]) == [6,4,8] | |
template <typename Container, typename UnaryPredicate> | |
Container take_last_while(UnaryPredicate pred, const Container& xs) { | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
const auto r_begin = internal::make_reverse_iterator(std::end(xs)); | |
const auto r_end = internal::make_reverse_iterator(std::begin(xs)); | |
const auto itFirstReverse = std::find_if(r_begin, r_end, logical_not(pred)); | |
if (itFirstReverse == r_begin) return Container(); | |
if (itFirstReverse == r_end) return xs; | |
return Container(itFirstReverse.base(), std::end(xs)); | |
} | |
// API search type: drop_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Remove elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// drop_while(is_even, [0,2,4,5,6,7,8]) == [5,6,7,8] | |
// Also known as trim_left_by. | |
template <typename Container, typename UnaryPredicate> | |
Container drop_while(UnaryPredicate pred, const Container& xs) { | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
auto itFirstNot = std::find_if_not(std::begin(xs), std::end(xs), pred); | |
if (itFirstNot == std::end(xs)) return Container(); | |
return Container(itFirstNot, std::end(xs)); | |
} | |
// API search type: drop_last_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Remove elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// drop_last_while(is_even, [0,2,7,5,6,4,8]) == [0,2,7,5] | |
template <typename Container, typename UnaryPredicate> | |
Container drop_last_while(UnaryPredicate pred, const Container& xs) { | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
const auto r_begin = internal::make_reverse_iterator(std::end(xs)); | |
const auto r_end = internal::make_reverse_iterator(std::begin(xs)); | |
const auto itFirstNotReverse = std::find_if_not(r_begin, r_end, pred); | |
if (itFirstNotReverse == r_begin) return xs; | |
if (itFirstNotReverse == r_end) return Container(); | |
return Container(std::begin(xs), itFirstNotReverse.base()); | |
} | |
// API search type: fold_left : (((a, b) -> a), a, [b]) -> a | |
// fwd bind count: 2 | |
// fold_left((+), 0, [1, 2, 3]) == ((0+1)+2)+3 == 6 | |
// Takes the second argument and the first item of the list | |
// and applies the function to them, | |
// then feeds the function with this result and the second argument and so on. | |
template <typename F, typename Container, typename Acc> | |
Acc fold_left(F f, const Acc& init, const Container& xs) { | |
using std::begin; | |
using std::end; | |
return internal::accumulate(begin(xs), end(xs), init, f); | |
} | |
// API search type: reduce : (((a, a) -> a), a, [a]) -> a | |
// fwd bind count: 2 | |
// reduce((+), 0, [1, 2, 3]) == (0+1+2+3) == 6 | |
// Combines the initial value and all elements of the sequence | |
// using the given function. | |
// The set of f, init and value_type should form a monoid. | |
template <typename F, typename Container> | |
typename Container::value_type reduce( | |
F f, const typename Container::value_type& init, const Container& xs) { | |
return fold_left(f, init, xs); | |
} | |
// API search type: fold_left_1 : (((a, a) -> a), [a]) -> a | |
// fwd bind count: 1 | |
// fold_left_1((+), [1, 2, 3]) == (1+2)+3 == 6 | |
// Takes the first 2 items of the list and applies the function to them, | |
// then feeds the function with this result and the third argument and so on. | |
// xs must be non-empty. | |
template <typename F, typename Container> | |
auto fold_left_1(F f, const Container& xs) { | |
assert(!xs.empty()); | |
using std::begin; | |
using std::end; | |
const auto it = begin(xs); | |
return internal::accumulate(std::next(it), end(xs), *it, f); | |
} | |
// API search type: reduce_1 : (((a, a) -> a), [a]) -> a | |
// fwd bind count: 1 | |
// reduce_1((+), [1, 2, 3]) == (1+2+3) == 6 | |
// Joins all elements of the sequence using the given function. | |
// The set of f and value_type should form a semigroup. | |
template <typename F, typename Container> | |
typename Container::value_type reduce_1(F f, const Container& xs) { | |
assert(!xs.empty()); | |
return fold_left_1(f, xs); | |
} | |
// API search type: fold_right : (((a, b) -> b), b) -> [a] -> b | |
// fwd bind count: 2 | |
// fold_right((+), 0, [1, 2, 3]) == 1+(2+(3+0)) == 6 | |
// Takes the second argument and the last item of the list | |
// and applies the function, | |
// then it takes the penultimate item from the end and the result, and so on. | |
template <typename F, typename Container, typename Acc> | |
Acc fold_right(F f, const Acc& init, const Container& xs) { | |
return internal::accumulate(xs.rbegin(), xs.rend(), init, flip(f)); | |
} | |
// API search type: fold_right_1 : (((a, a) -> a), [a]) -> a | |
// fwd bind count: 1 | |
// fold_right_1((+), [1, 2, 3]) == 1+(2+3)) == 6 | |
// Takes the last two items of the list and applies the function, | |
// then it takes the third item from the end and the result, and so on. | |
template <typename F, typename Container> | |
auto fold_right_1(F f, const Container& xs) { | |
assert(!xs.empty()); | |
const auto it = xs.rbegin(); | |
return internal::accumulate(std::next(it), xs.rend(), *it, flip(f)); | |
} | |
// API search type: scan_left : (((a, b) -> a), a, [b]) -> [a] | |
// fwd bind count: 2 | |
// scan_left((+), 0, [1, 2, 3]) == [0, 1, 3, 6] | |
// Takes the second argument and the first item of the list | |
// and applies the function to them, | |
// then feeds the function with this result and the second argument and so on. | |
// It returns the list of intermediate and final results. | |
template <typename F, typename ContainerIn, typename Acc> | |
auto scan_left(F f, const Acc& init, const ContainerIn& xs) { | |
using ContainerOut = | |
typename internal::same_cont_new_t<ContainerIn, Acc, 1>::type; | |
ContainerOut result; | |
internal::prepare_container(result, size_of_cont(xs) + 1); | |
using std::begin; | |
using std::end; | |
internal::scan_impl(f, init, internal::get_back_inserter(result), begin(xs), | |
end(xs)); | |
return result; | |
} | |
// API search type: scan_left_1 : (((a, a) -> a), [a]) -> [a] | |
// fwd bind count: 1 | |
// scan_left_1((+), [1, 2, 3]) == [1, 3, 6] | |
// Takes the first 2 items of the list and applies the function to them, | |
// then feeds the function with this result and the third argument and so on. | |
// It returns the list of intermediate and final results. | |
// xs must be non-empty. | |
template <typename F, typename ContainerIn> | |
auto scan_left_1(F f, const ContainerIn& xs) { | |
assert(!xs.empty()); | |
using std::begin; | |
using std::end; | |
const auto beginIt = begin(xs); | |
using ContainerOut = typename internal::same_cont_new_t< | |
ContainerIn, internal::uncvref_t<decltype(*beginIt)>, 0>::type; | |
ContainerOut result; | |
internal::prepare_container(result, size_of_cont(xs)); | |
internal::scan_impl(f, *beginIt, internal::get_back_inserter(result), | |
std::next(beginIt), end(xs)); | |
return result; | |
} | |
// API search type: scan_right : (((a, b) -> b), b, [a]) -> [b] | |
// fwd bind count: 2 | |
// scan_right((+), 0, [1, 2, 3]) == [6, 5, 3, 0] | |
// Takes the second argument and the last item of the list | |
// and applies the function, | |
// then it takes the penultimate item from the end and the result, and so on. | |
// It returns the list of intermediate and final results. | |
template < | |
typename F, typename ContainerIn, | |
typename Acc = typename utils::function_traits<F>::template arg<1>::type, | |
typename ContainerOut = | |
typename internal::same_cont_new_t<ContainerIn, Acc, 1>::type> | |
ContainerOut scan_right(F f, const Acc& init, const ContainerIn& xs) { | |
return reverse(scan_left(flip(f), init, reverse(xs))); | |
} | |
// API search type: scan_right_1 : (((a, a) -> a), [a]) -> [a] | |
// fwd bind count: 1 | |
// scan_right_1((+), [1, 2, 3]) == [6, 5, 3] | |
// Takes the last two items of the list and applies the function, | |
// then it takes the third item from the end and the result, and so on. | |
// It returns the list of inntermediate and final results. | |
template <typename F, typename ContainerIn, | |
typename Acc = typename ContainerIn::value_type, | |
typename ContainerOut = | |
typename internal::same_cont_new_t<ContainerIn, Acc, 0>::type> | |
ContainerOut scan_right_1(F f, const ContainerIn& xs) { | |
return reverse(scan_left_1(flip(f), reverse(xs))); | |
} | |
// API search type: sum : [a] -> a | |
// fwd bind count: 0 | |
// Adds up all values in a sequence. | |
// sum([0,3,1]) == 4 | |
// sum([]) == 0 | |
template <typename Container, typename T = typename Container::value_type> | |
T sum(const Container& xs) { | |
T result = T(); | |
for (const auto& x : xs) { | |
result = result + x; | |
} | |
return result; | |
} | |
// API search type: product : [a] -> a | |
// fwd bind count: 0 | |
// Returns the product of all values in a sequence. | |
// product([3,1,2]) == 6 | |
// product([]) == 1 | |
template <typename Container, typename T = typename Container::value_type> | |
T product(const Container& xs) { | |
T result{1}; | |
for (const auto& x : xs) { | |
result = result * x; | |
} | |
return result; | |
} | |
namespace internal { | |
template <typename T, typename Container> | |
Container append_elem(internal::reuse_container_t, const T& y, Container&& xs) { | |
*internal::get_back_inserter(xs) = y; | |
return std::forward<Container>(xs); | |
} | |
template <typename T, typename Container> | |
Container append_elem(internal::create_new_container_t, const T& y, | |
const Container& xs) { | |
Container result; | |
internal::prepare_container(result, size_of_cont(xs) + 1); | |
std::copy(std::begin(xs), std::end(xs), internal::get_back_inserter(result)); | |
*internal::get_back_inserter(result) = y; | |
return result; | |
} | |
} // namespace internal | |
// API search type: append_elem : (a, [a]) -> [a] | |
// fwd bind count: 1 | |
// Extends a sequence with one element at the back. | |
// append_elem([1, 2], 3) == [1, 2, 3] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>, | |
typename T = typename ContainerOut::value_type> | |
ContainerOut append_elem(const T& y, Container&& xs) { | |
return internal::append_elem(internal::can_reuse_v<Container>{}, y, | |
std::forward<Container>(xs)); | |
} | |
namespace internal { | |
template <typename T> | |
std::list<T> prepend_elem(internal::reuse_container_t, const T& y, | |
std::list<T>&& xs) { | |
xs.push_front(y); | |
return std::forward<std::list<T>>(xs); | |
} | |
template <typename T, typename Container> | |
Container prepend_elem(internal::reuse_container_t, const T& y, | |
Container&& xs) { | |
xs.resize(size_of_cont(xs) + 1); | |
std::copy(++xs.rbegin(), xs.rend(), xs.rbegin()); | |
*std::begin(xs) = y; | |
return std::forward<Container>(xs); | |
} | |
template <typename T, typename Container> | |
Container prepend_elem(internal::create_new_container_t, const T& y, | |
const Container& xs) { | |
Container result; | |
internal::prepare_container(result, size_of_cont(xs) + 1); | |
*internal::get_back_inserter(result) = y; | |
std::copy(std::begin(xs), std::end(xs), internal::get_back_inserter(result)); | |
return result; | |
} | |
} // namespace internal | |
// API search type: prepend_elem : (a, [a]) -> [a] | |
// fwd bind count: 1 | |
// Extends a sequence with one element in the front. | |
// prepend_elem([2, 3], 1) == [1, 2, 3] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>, | |
typename T = typename ContainerOut::value_type> | |
ContainerOut prepend_elem(const T& y, Container&& xs) { | |
return internal::prepend_elem(internal::can_reuse_v<Container>{}, y, | |
std::forward<Container>(xs)); | |
} | |
// API search type: append : ([a], [a]) -> [a] | |
// fwd bind count: 1 | |
// Concatenates two sequences. | |
// append([1, 2], [3, 4, 5]) == [1, 2, 3, 4, 5] | |
template <typename ContainerIn1, typename ContainerIn2 = ContainerIn1, | |
typename ContainerOut = ContainerIn1> | |
ContainerOut append(const ContainerIn1& xs, const ContainerIn2& ys) { | |
ContainerOut result; | |
internal::prepare_container(result, size_of_cont(xs) + size_of_cont(ys)); | |
std::copy(std::begin(xs), std::end(xs), internal::get_back_inserter(result)); | |
std::copy(std::begin(ys), std::end(ys), internal::get_back_inserter(result)); | |
return result; | |
} | |
// API search type: append_convert : ([a], [a]) -> [a] | |
// fwd bind count: 1 | |
// Same as append, but makes it easier to | |
// use an output container type different from the input container type. | |
template <typename ContainerOut, typename ContainerIn1, | |
typename ContainerIn2 = ContainerIn1> | |
ContainerOut append_convert(const ContainerIn1& xs, const ContainerIn2& ys) { | |
return append<ContainerIn1, ContainerIn2, ContainerOut>(xs, ys); | |
} | |
// API search type: concat : [[a]] -> [a] | |
// fwd bind count: 0 | |
// Concatenates multiple sequences. | |
// concat([[1, 2], [], [3]]) == [1, 2, 3] | |
// Also known as flatten. | |
template <typename ContainerIn, | |
typename ContainerOut = typename ContainerIn::value_type> | |
ContainerOut concat(const ContainerIn& xss) { | |
std::size_t length = | |
sum(transform(size_of_cont<typename ContainerIn::value_type>, xss)); | |
ContainerOut result; | |
internal::prepare_container(result, length); | |
using std::begin; | |
using std::end; | |
for (const auto& xs : xss) { | |
result.insert(end(result), begin(xs), end(xs)); | |
} | |
return result; | |
} | |
// API search type: interweave : ([a], [a]) -> [a] | |
// fwd bind count: 1 | |
// Return a sequence that contains elements from the two provided sequences | |
// in alternating order. If one list runs out of items, | |
// appends the items from the remaining list. | |
// interweave([1,3], [2,4]) == [1,2,3,4] | |
// interweave([1,3,5,7], [2,4]) == [1,2,3,4,5,7] | |
// See interleave for interweaving more than two sequences. | |
template <typename Container> | |
Container interweave(const Container& xs, const Container& ys) { | |
Container result; | |
internal::prepare_container(result, size_of_cont(xs) + size_of_cont(ys)); | |
auto it = internal::get_back_inserter<Container>(result); | |
auto it_xs = std::begin(xs); | |
auto it_ys = std::begin(ys); | |
while (it_xs != std::end(xs) || it_ys != std::end(ys)) { | |
if (it_xs != std::end(xs)) *it = *(it_xs++); | |
if (it_ys != std::end(ys)) *it = *(it_ys++); | |
} | |
return result; | |
} | |
// API search type: unweave : [a] -> ([a], [a]) | |
// fwd bind count: 0 | |
// Puts the elements with an even index into the first list, | |
// and the elements with an odd index into the second list. | |
// Inverse of interweave. | |
// unweave([0,1,2,3]) == ([0,2], [1,3]) | |
// unweave([0,1,2,3,4]) == ([0,2,4], [1,3]) | |
template <typename Container> | |
std::pair<Container, Container> unweave(const Container& xs) { | |
std::pair<Container, Container> result; | |
if (is_even(size_of_cont(xs))) | |
internal::prepare_container(result.first, size_of_cont(xs) / 2); | |
else | |
internal::prepare_container(result.first, size_of_cont(xs) / 2 + 1); | |
internal::prepare_container(result.second, size_of_cont(xs) / 2); | |
auto it_even = internal::get_back_inserter<Container>(result.first); | |
auto it_odd = internal::get_back_inserter<Container>(result.second); | |
std::size_t counter = 0; | |
for (const auto& x : xs) { | |
if (counter++ % 2 == 0) | |
*it_even = x; | |
else | |
*it_odd = x; | |
} | |
return result; | |
} | |
namespace internal { | |
template <typename Compare, typename T> | |
std::list<T> sort_by(internal::reuse_container_t, Compare comp, | |
std::list<T>&& xs) { | |
xs.sort(comp); | |
return std::forward<std::list<T>>(xs); | |
} | |
template <typename Compare, typename T> | |
std::list<T> sort_by(internal::create_new_container_t, Compare comp, | |
const std::list<T>& xs) { | |
auto result = xs; | |
result.sort(comp); | |
return result; | |
} | |
template <typename Compare, typename Container> | |
Container sort_by(internal::reuse_container_t, Compare comp, Container&& xs) { | |
std::sort(std::begin(xs), std::end(xs), comp); | |
return std::forward<Container>(xs); | |
} | |
template <typename Compare, typename Container> | |
Container sort_by(internal::create_new_container_t, Compare comp, | |
const Container& xs) { | |
auto result = xs; | |
std::sort(std::begin(result), std::end(result), comp); | |
return result; | |
} | |
} // namespace internal | |
// API search type: sort_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence by given less comparator. | |
template <typename Compare, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut sort_by(Compare comp, Container&& xs) { | |
return internal::sort_by(internal::can_reuse_v<Container>{}, comp, | |
std::forward<Container>(xs)); | |
} | |
namespace internal { | |
// workarounds for clang bug 24115 | |
// (std::sort and std::unique with std::function as comp) | |
// https://llvm.org/bugs/show_bug.cgi?id=24115 | |
template <typename F> | |
struct is_less_by_struct { | |
is_less_by_struct(F f) : f_(f){}; | |
template <typename T> | |
bool operator()(const T& x, const T& y) { | |
return f_(x) < f_(y); | |
} | |
private: | |
F f_; | |
}; | |
template <typename F> | |
struct is_equal_by_struct { | |
is_equal_by_struct(F f) : f_(f){}; | |
template <typename T> | |
bool operator()(const T& x, const T& y) { | |
return f_(x) == f_(y); | |
} | |
private: | |
F f_; | |
}; | |
} // namespace internal | |
// API search type: sort_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence by a given transformer. | |
template <typename F, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut sort_on(F f, Container&& xs) { | |
return sort_by(internal::is_less_by_struct<F>(f), | |
std::forward<Container>(xs)); | |
} | |
// API search type: sort : [a] -> [a] | |
// fwd bind count: 0 | |
// Sort a sequence to ascending order using std::less. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut sort(Container&& xs) { | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return sort_by(std::less<T>(), std::forward<Container>(xs)); | |
} | |
namespace internal { | |
template <typename Compare, typename T> | |
std::list<T> stable_sort_by(internal::reuse_container_t, Compare comp, | |
std::list<T>&& xs) { | |
xs.sort(comp); // std::list<T>::sort ist already stable. | |
return std::forward<std::list<T>>(xs); | |
} | |
template <typename Compare, typename T> | |
std::list<T> stable_sort_by(internal::create_new_container_t, Compare comp, | |
const std::list<T>& xs) { | |
auto result = xs; | |
result.sort(comp); // std::list<T>::sort ist already stable. | |
return result; | |
} | |
template <typename Compare, typename Container> | |
Container stable_sort_by(internal::reuse_container_t, Compare comp, | |
Container&& xs) { | |
std::sort(std::begin(xs), std::end(xs), comp); | |
return std::forward<Container>(xs); | |
} | |
template <typename Compare, typename Container> | |
Container stable_sort_by(internal::create_new_container_t, Compare comp, | |
const Container& xs) { | |
auto result = xs; | |
std::sort(std::begin(result), std::end(result), comp); | |
return result; | |
} | |
} // namespace internal | |
// API search type: stable_sort_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence stably by given less comparator. | |
template <typename Compare, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut stable_sort_by(Compare comp, Container&& xs) { | |
return internal::stable_sort_by(internal::can_reuse_v<Container>{}, comp, | |
std::forward<Container>(xs)); | |
} | |
// API search type: stable_sort_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence stably by given transformer. | |
template <typename F, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut stable_sort_on(F f, Container&& xs) { | |
return stable_sort_by(internal::is_less_by_struct<F>(f), | |
std::forward<Container>(xs)); | |
} | |
// API search type: stable_sort : [a] -> [a] | |
// fwd bind count: 0 | |
// Sort a sequence stably to ascending order using std::less. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut stable_sort(Container&& xs) { | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return stable_sort_by(std::less<T>(), std::forward<Container>(xs)); | |
} | |
namespace internal { | |
template <typename Compare, typename Container> | |
Container partial_sort_by(internal::reuse_container_t, Compare comp, | |
std::size_t count, Container&& xs) { | |
if (count > xs.size()) { | |
count = xs.size(); | |
} | |
auto middle = std::begin(xs); | |
internal::advance_iterator(middle, count); | |
std::partial_sort(std::begin(xs), middle, std::end(xs), comp); | |
return std::forward<Container>( | |
get_segment(internal::reuse_container_t(), 0, count, xs)); | |
} | |
template <typename Compare, typename Container> | |
Container partial_sort_by(internal::create_new_container_t, Compare comp, | |
std::size_t count, const Container& xs) { | |
auto result = xs; | |
return partial_sort_by(internal::reuse_container_t(), comp, count, | |
std::move(result)); | |
} | |
} // namespace internal | |
// API search type: partial_sort_by : (((a, a) -> Bool), Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Partially sort a sequence by a given less comparator. | |
// Returns only the sorted segment. | |
template <typename Compare, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut partial_sort_by(Compare comp, std::size_t count, Container&& xs) { | |
return internal::partial_sort_by(internal::can_reuse_v<Container>{}, comp, | |
count, std::forward<Container>(xs)); | |
} | |
// API search type: partial_sort_on : ((a -> b), Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Partially sort a sequence by a given transformer. | |
// Returns only the sorted segment. | |
template <typename F, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut partial_sort_on(F f, std::size_t count, Container&& xs) { | |
return partial_sort_by(internal::is_less_by_struct<F>(f), count, | |
std::forward<Container>(xs)); | |
} | |
// API search type: partial_sort : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Partially sort a sequence in ascending order using std::less. | |
// Returns only the sorted segment. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut partial_sort(std::size_t count, Container&& xs) { | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return partial_sort_by(std::less<T>(), count, std::forward<Container>(xs)); | |
} | |
// API search type: nth_element_by : (((a, a) -> Bool), Int, [a]) -> a | |
// fwd bind count: 2 | |
// Return the nth largest element of a sequence by a given less comparator. | |
template <typename Compare, typename Container, | |
typename T = typename Container::value_type> | |
T nth_element_by(Compare comp, std::size_t n, const Container& xs) { | |
assert(n < xs.size()); | |
auto result = xs; | |
auto middle = std::begin(result); | |
internal::advance_iterator(middle, n); | |
std::nth_element(std::begin(result), middle, std::end(result), comp); | |
return *middle; | |
} | |
// API search type: nth_element_on : ((a -> b), Int, [a]) -> a | |
// fwd bind count: 2 | |
// Return the nth largest element of a sequence by a given transformer. | |
template <typename F, typename Container, | |
typename T = typename Container::value_type> | |
T nth_element_on(F f, std::size_t n, const Container& xs) { | |
return nth_element_by(internal::is_less_by_struct<F>(f), n, xs); | |
} | |
// API search type: nth_element : (Int, [a]) -> a | |
// fwd bind count: 1 | |
// Return the nth largest element of a sequence using std::less. | |
template <typename Container, typename T = typename Container::value_type> | |
T nth_element(std::size_t n, const Container& xs) { | |
return nth_element_by(std::less<T>(), n, xs); | |
} | |
namespace internal { | |
template <typename BinaryPredicate, typename Container> | |
Container unique_by(internal::reuse_container_t, BinaryPredicate pred, | |
Container&& xs) { | |
internal::check_binary_predicate_for_container<BinaryPredicate, Container>(); | |
const auto it_end = std::unique(std::begin(xs), std::end(xs), pred); | |
xs.erase(it_end, std::end(xs)); | |
return std::forward<Container>(xs); | |
} | |
template <typename BinaryPredicate, typename Container> | |
Container unique_by(internal::create_new_container_t, BinaryPredicate pred, | |
const Container& xs) { | |
auto result = xs; | |
return unique_by(internal::reuse_container_t(), pred, std::move(result)); | |
} | |
} // namespace internal | |
// API search type: unique_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Like unique, eliminates all but the first element | |
// from every consecutive group of equivalent elements from the sequence; | |
// but with a user supplied equality predicate. | |
// See nub_by for making elements globally unique in a sequence. | |
// O(n) | |
template <typename BinaryPredicate, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut unique_by(BinaryPredicate pred, Container&& xs) { | |
return internal::unique_by(internal::can_reuse_v<Container>{}, pred, | |
std::forward<Container>(xs)); | |
} | |
// API search type: unique_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Like unique, eliminates all but the first element | |
// from every consecutive group of equivalent elements from the sequence; | |
// but with a user supplied transformation (e.g. getter). | |
// See nub_on for making elements globally unique in a sequence. | |
// O(n) | |
// Also known as drop_repeats. | |
template <typename F, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut unique_on(F f, Container&& xs) { | |
return unique_by(internal::is_equal_by_struct<F>(f), | |
std::forward<Container>(xs)); | |
} | |
// API search type: unique : [a] -> [a] | |
// fwd bind count: 0 | |
// Eliminates all but the first element | |
// from every consecutive group of equivalent elements from the sequence. | |
// unique([1,2,2,3,2]) == [1,2,3,2] | |
// See nub for making elements globally unique in a sequence. | |
// O(n) | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut unique(Container&& xs) { | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return unique_on(identity<T>, std::forward<Container>(xs)); | |
} | |
// API search type: intersperse : (a, [a]) -> [a] | |
// fwd bind count: 1 | |
// Insert a value between all adjacent values in a sequence. | |
// intersperse(0, [1, 2, 3]) == [1, 0, 2, 0, 3] | |
// Also known as interpose. | |
template <typename Container, typename X = typename Container::value_type> | |
Container intersperse(const X& value, const Container& xs) { | |
if (xs.empty()) return Container(); | |
if (size_of_cont(xs) == 1) return xs; | |
Container result; | |
internal::prepare_container( | |
result, std::max<std::size_t>(0, size_of_cont(xs) * 2 - 1)); | |
auto it = internal::get_back_inserter(result); | |
for_each(std::begin(xs), --std::end(xs), [&value, &it](const X& x) { | |
*it = x; | |
*it = value; | |
}); | |
*it = xs.back(); | |
return result; | |
} | |
// API search type: join : ([a], [[a]]) -> [a] | |
// fwd bind count: 1 | |
// Inserts a separator sequence in between the elements | |
// of a sequence of sequences and concatenates the result. | |
// Also known as intercalate or implode. | |
// join(", ", ["a", "bee", "cee"]) == "a, bee, cee" | |
// join([0, 0], [[1], [2], [3, 4]]) == [1, 0, 0, 2, 0, 0, 3, 4] | |
template <typename Container, typename X = typename Container::value_type> | |
X join(const X& separator, const Container& xs) { | |
return concat(intersperse(separator, xs)); | |
} | |
// API search type: join_elem : (a, [[a]]) -> [a] | |
// fwd bind count: 1 | |
// Inserts a separator in between the elements | |
// of a sequence of sequences and concatenates the result. | |
// Also known as intercalate_elem. | |
// join_elem(';', ["a", "bee", "cee"]) == "a;bee;cee" | |
// join_elem(0, [[1], [2], [3, 4]]) == [1, 0, 2, 0, 3, 4]] | |
template <typename Container, typename Inner = typename Container::value_type, | |
typename X = typename Inner::value_type> | |
Inner join_elem(const X& separator, const Container& xs) { | |
return join(Inner(1, separator), xs); | |
} | |
// API search type: is_elem_of_by : ((a -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if at least one element of the container fulfils a predicate. | |
// is_elem_of_by((==), [1,2,3]) == true | |
template <typename UnaryPredicate, typename Container> | |
bool is_elem_of_by(UnaryPredicate pred, const Container& xs) { | |
return std::find_if(std::begin(xs), std::end(xs), pred) != std::end(xs); | |
} | |
// API search type: is_elem_of : (a, [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if an element is a member of a container. | |
// is_elem_of(2, [1,2,3]) == true | |
// Also known as flip(contains). | |
template <typename Container> | |
bool is_elem_of(const typename Container::value_type& x, const Container& xs) { | |
return is_elem_of_by(is_equal_to(x), xs); | |
} | |
// API search type: nub_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Makes the elements in a container unique with respect to a predicate | |
// nub_by((==), [1,2,2,3,2]) == [1,2,3] | |
// O(n^2) | |
template <typename Container, typename BinaryPredicate> | |
Container nub_by(BinaryPredicate p, const Container& xs) { | |
Container result; | |
auto itOut = internal::get_back_inserter(result); | |
for (const auto& x : xs) { | |
auto eqToX = bind_1st_of_2(p, x); | |
if (!is_elem_of_by(eqToX, result)) { | |
*itOut = x; | |
} | |
} | |
return result; | |
} | |
// API search type: nub_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Makes the elements in a container unique | |
// with respect to their function value. | |
// nub_on((mod 10), [12,32,15]) == [12,15] | |
// O(n^2) | |
template <typename Container, typename F> | |
Container nub_on(F f, const Container& xs) { | |
return nub_by(is_equal_by(f), xs); | |
} | |
// API search type: nub : [a] -> [a] | |
// fwd bind count: 0 | |
// Makes the elements in a container unique. | |
// nub([1,2,2,3,2]) == [1,2,3] | |
// O(n^2) | |
// Also known as distinct. | |
template <typename Container> | |
Container nub(const Container& xs) { | |
typedef typename Container::value_type T; | |
return nub_by(std::equal_to<T>(), xs); | |
} | |
// API search type: all_unique_by_eq : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all elements in a container are unique | |
// with respect to a predicate. | |
// Returns true for empty containers. | |
// O(n^2) | |
template <typename Container, typename BinaryPredicate> | |
bool all_unique_by_eq(BinaryPredicate p, const Container& xs) { | |
internal::check_binary_predicate_for_container<BinaryPredicate, Container>(); | |
return size_of_cont(nub_by(p, xs)) == size_of_cont(xs); | |
} | |
// API search type: all_unique_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all elements in a container are unique | |
// with respect to their function values. | |
// Returns true for empty containers. | |
// O(n^2) | |
template <typename Container, typename F> | |
bool all_unique_on(F f, const Container& xs) { | |
return all_unique_by_eq(is_equal_by(f), xs); | |
} | |
// API search type: all_unique : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if all elements in a container are unique. | |
// Returns true for empty containers. | |
// O(n^2) | |
template <typename Container> | |
bool all_unique(const Container& xs) { | |
typedef typename Container::value_type T; | |
auto comp = std::equal_to<T>(); | |
return all_unique_by_eq(comp, xs); | |
} | |
// API search type: is_strictly_sorted_by : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is strictly sorted using a predicate. | |
// comp(a, b) must return true only if a < b. | |
// O(n) | |
template <typename Container, typename Compare> | |
bool is_strictly_sorted_by(Compare comp, const Container& xs) { | |
internal::check_compare_for_container<Compare, Container>(); | |
if (size_of_cont(xs) < 2) return true; | |
auto it1 = std::begin(xs); | |
for (auto it2 = it1 + 1; it2 < std::end(xs); ++it1, ++it2) | |
if (!internal::invoke(comp, *it1, *it2)) return false; | |
return true; | |
} | |
// API search type: is_strictly_sorted_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is strictly sorted using a transformer. | |
// O(n) | |
template <typename Container, typename F> | |
bool is_strictly_sorted_on(F f, const Container& xs) { | |
return is_strictly_sorted_by(is_less_by(f), xs); | |
} | |
// API search type: is_strictly_sorted : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if a container already is strictly sorted | |
// in ascending order using std::less. | |
// O(n) | |
template <typename Container> | |
bool is_strictly_sorted(const Container& xs) { | |
typedef typename Container::value_type T; | |
auto comp = std::less<T>(); | |
return is_strictly_sorted_by(comp, xs); | |
} | |
// API search type: is_sorted_by : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is sorted using a predicate. | |
// comp(a, b) must return true only if a < b. | |
// O(n) | |
template <typename Container, typename Compare> | |
bool is_sorted_by(Compare comp, const Container& xs) { | |
internal::check_compare_for_container<Compare, Container>(); | |
if (size_of_cont(xs) < 2) return true; | |
auto it1 = std::begin(xs); | |
for (auto it2 = it1 + 1; it2 < std::end(xs); ++it1, ++it2) | |
if (internal::invoke(comp, *it2, *it1)) return false; | |
return true; | |
} | |
// API search type: is_sorted_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is strictly sorted using a transformer. | |
// O(n) | |
template <typename Container, typename F> | |
bool is_sorted_on(F f, const Container& xs) { | |
return is_sorted_by(is_less_by(f), xs); | |
} | |
// API search type: is_sorted : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if a container already is sorted | |
// in ascending order using std::less. | |
// O(n) | |
template <typename Container> | |
bool is_sorted(const Container& xs) { | |
typedef typename Container::value_type T; | |
auto comp = std::less<T>(); | |
return is_sorted_by(comp, xs); | |
} | |
// API search type: is_prefix_of : ([a], [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a containers starts with a token. | |
// is_prefix_of("Fun", "FunctionalPlus") == true | |
template <typename Container> | |
bool is_prefix_of(const Container& token, const Container& xs) { | |
if (size_of_cont(token) > size_of_cont(xs)) return false; | |
return get_segment(0, size_of_cont(token), xs) == token; | |
} | |
// API search type: is_suffix_of : ([a], [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a containers contains a token as a segment. | |
// is_suffix_of("us", "FunctionalPlus") == true | |
template <typename Container> | |
bool is_suffix_of(const Container& token, const Container& xs) { | |
if (size_of_cont(token) > size_of_cont(xs)) return false; | |
return get_segment(size_of_cont(xs) - size_of_cont(token), size_of_cont(xs), | |
xs) == token; | |
} | |
// API search type: all_by : ((a -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a containers contains a token as a segment. | |
// all_by(is_even, [2, 4, 6]) == true | |
// Returns true for empty containers. | |
template <typename UnaryPredicate, typename Container> | |
bool all_by(UnaryPredicate p, const Container& xs) { | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
return std::all_of(std::begin(xs), std::end(xs), p); | |
} | |
// API search type: all : [Bool] -> Bool | |
// fwd bind count: 0 | |
// Checks if all values in a container evaluate to true. | |
// all([true, false, true]) == false | |
// Returns true for empty containers. | |
template <typename Container> | |
bool all(const Container& xs) { | |
typedef typename Container::value_type T; | |
return all_by(identity<T>, xs); | |
} | |
// API search type: all_the_same_by : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all values in a container are equal using a binary predicate. | |
// Returns true for empty containers. | |
template <typename Container, typename BinaryPredicate> | |
bool all_the_same_by(BinaryPredicate p, const Container& xs) { | |
internal::check_binary_predicate_for_container<BinaryPredicate, Container>(); | |
if (size_of_cont(xs) < 2) return true; | |
auto unaryPredicate = bind_1st_of_2(p, xs.front()); | |
return all_by(unaryPredicate, xs); | |
} | |
// API search type: all_the_same_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all values in a container are equal using a transformer. | |
// Returns true for empty containers. | |
template <typename Container, typename F> | |
bool all_the_same_on(F f, const Container& xs) { | |
if (size_of_cont(xs) < 2) return true; | |
auto unaryPredicate = is_equal_by_to(f, f(xs.front())); | |
return all_by(unaryPredicate, xs); | |
} | |
// API search type: all_the_same : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if all values in a container are equal. | |
// Returns true for empty containers. | |
template <typename Container> | |
bool all_the_same(const Container& xs) { | |
typedef typename Container::value_type T; | |
auto binaryPredicate = std::equal_to<T>(); | |
return all_the_same_by(binaryPredicate, xs); | |
} | |
// API search type: numbers_step : (a, a, a) -> [a] | |
// fwd bind count: 2 | |
// Return a sequence of numbers using a specific step. | |
// numbers_step(2, 9, 2) == [2, 4, 6, 8] | |
template <typename T, typename ContainerOut = std::vector<T>> | |
ContainerOut numbers_step(const T start, const T end, const T step) { | |
ContainerOut result; | |
if ((step > 0 && start >= end) || (step < 0 && start <= end) || step == 0) { | |
return result; | |
} | |
std::size_t size = static_cast<std::size_t>((end - start) / step); | |
internal::prepare_container(result, size); | |
auto it = internal::get_back_inserter<ContainerOut>(result); | |
for (T x = start; x < end; x += step) *it = x; | |
return result; | |
} | |
// API search type: numbers : (a, a) -> [a] | |
// fwd bind count: 1 | |
// Return an ascending sequence of numbers.. | |
// Also known as range. | |
// numbers(2, 9) == [2, 3, 4, 5, 6, 7, 8] | |
template <typename T, typename ContainerOut = std::vector<T>> | |
ContainerOut numbers(const T start, const T end) { | |
return numbers_step<T, ContainerOut>(start, end, 1); | |
} | |
// API search type: singleton_seq : a -> [a] | |
// fwd bind count: 0 | |
// Construct a sequence containing a single value. | |
// singleton_seq(3) == [3]. | |
template <typename T, typename ContainerOut = std::vector<T>> | |
ContainerOut singleton_seq(const T& x) { | |
return ContainerOut(1, x); | |
} | |
// API search type: all_idxs : [a] -> [Int] | |
// fwd bind count: 0 | |
// Returns a vector containing all valid indices of sequence xs. | |
// all_idxs([6,4,7,6]) == [0,1,2,3] | |
template <typename Container> | |
std::vector<std::size_t> all_idxs(const Container& xs) { | |
return numbers<std::size_t>(0, size_of_cont(xs)); | |
} | |
// API search type: init : [a] -> [a] | |
// fwd bind count: 0 | |
// init([0,1,2,3]) == [0,1,2] | |
// Unsafe! xs must be non-empty. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut init(Container&& xs) { | |
assert(!is_empty(xs)); | |
return get_segment(0, size_of_cont(std::forward<Container>(xs)) - 1, xs); | |
} | |
// API search type: tail : [a] -> [a] | |
// fwd bind count: 0 | |
// Drops the first element of a container, keeps the rest. Unsafe! | |
// tail([0,1,2,3]) == [1,2,3] | |
// Unsafe! xs must be non-empty. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut tail(Container&& xs) { | |
assert(!is_empty(xs)); | |
return get_segment(1, size_of_cont(std::forward<Container>(xs)), xs); | |
} | |
// API search type: head : [a] -> a | |
// fwd bind count: 0 | |
// Return the first element of a container. | |
// head([0,1,2,3]) == 0 | |
// Unsafe! xs must be non-empty. | |
template <typename Container> | |
typename Container::value_type head(const Container& xs) { | |
assert(!is_empty(xs)); | |
return xs.front(); | |
} | |
// API search type: last : [a] -> a | |
// fwd bind count: 0 | |
// Return the last element of a container. | |
// last([0,1,2,3]) == 3 | |
// Unsafe! xs must be non-empty. | |
template <typename Container> | |
typename Container::value_type last(const Container& xs) { | |
assert(!is_empty(xs)); | |
return xs.back(); | |
} | |
// API search type: mean_stddev : [a] -> (a, a) | |
// fwd bind count: 0 | |
// Calculates the mean and the population standard deviation. | |
// mean_stddev([4, 8]) == (6, 2) | |
// mean_stddev([1, 3, 7, 4]) == (3.75, 2.5) | |
// xs must be non-empty. | |
template <typename Result, typename Container> | |
std::pair<Result, Result> mean_stddev(const Container& xs) { | |
assert(size_of_cont(xs) != 0); | |
// http://stackoverflow.com/a/7616783/1866775 | |
Result sum = static_cast<Result>(internal::accumulate( | |
xs.begin(), xs.end(), static_cast<typename Container::value_type>(0))); | |
Result mean = sum / static_cast<Result>(xs.size()); | |
std::vector<Result> diff(xs.size()); | |
std::transform(xs.begin(), xs.end(), diff.begin(), | |
[mean](Result x) { return x - mean; }); | |
Result sq_sum = std::inner_product(diff.begin(), diff.end(), diff.begin(), | |
static_cast<Result>(0)); | |
Result stddev = std::sqrt(sq_sum / static_cast<Result>(xs.size())); | |
return std::make_pair(mean, stddev); | |
} | |
// API search type: count_occurrences_by : ((a -> b), [a]) -> Map b Int | |
// fwd bind count: 1 | |
// Returns a discrete frequency distribution of the elements in a container | |
// applying a specific transformer. | |
// count_occurrences_by(floor, [1.1, 2.3, 2.7, 3.6, 2.4]) == [(1, 1), (2, 3), | |
// (3, 1)] O(n) | |
template <typename F, typename ContainerIn> | |
auto count_occurrences_by(F f, const ContainerIn& xs) { | |
using In = typename ContainerIn::value_type; | |
using MapOut = | |
std::map<std::decay_t<internal::invoke_result_t<F, In>>, std::size_t>; | |
internal::trigger_static_asserts<internal::unary_function_tag, F, | |
typename ContainerIn::value_type>(); | |
MapOut result; | |
for (const auto& x : xs) { | |
++result[internal::invoke(f, x)]; | |
} | |
return result; | |
} | |
// API search type: count_occurrences : [a] -> Map a Int | |
// fwd bind count: 0 | |
// Returns a discrete frequency distribution of the elements in a container | |
// applying a specific transformer. | |
// Can be used to create a histogram. | |
// count_occurrences([1,2,2,3,2]) == [(1, 1), (2, 3), (3, 1)] | |
// O(n) | |
template <typename ContainerIn, | |
typename MapOut = | |
typename std::map<typename ContainerIn::value_type, std::size_t>> | |
MapOut count_occurrences(const ContainerIn& xs) { | |
return count_occurrences_by(identity<typename ContainerIn::value_type>, xs); | |
} | |
// API search type: lexicographical_less_by : (((a, a) -> Bool), [a], [a]) -> | |
// Bool fwd bind count: 2 Lexicographical less-than comparison using a specific | |
// predicate. lexicographical_less_by((<), [0,1,2,2,4,5], [0,1,2,3,4,5]) == true | |
// lexicographical_less_by((<), "012245", "012345") == true | |
// lexicographical_less_by((<), "01234", "012345") == true | |
// lexicographical_less_by((<), "012345", "01234") == false | |
// lexicographical_less_by((<), "012345", "012345") == false | |
template <typename Container, typename BinaryPredicate> | |
bool lexicographical_less_by(BinaryPredicate p, const Container& xs, | |
const Container& ys) { | |
internal::check_binary_predicate_for_container<BinaryPredicate, Container>(); | |
auto itXs = std::begin(xs); | |
auto itYs = std::begin(ys); | |
while (itXs != std::end(xs) && itYs != std::end(ys)) { | |
if (internal::invoke(p, *itXs, *itYs)) { | |
return true; | |
} | |
if (internal::invoke(p, *itYs, *itXs)) { | |
return false; | |
} | |
++itXs; | |
++itYs; | |
} | |
if (size_of_cont(xs) < size_of_cont(ys)) { | |
return true; | |
} | |
return false; | |
} | |
// API search type: lexicographical_less : ([a], [a]) -> Bool | |
// fwd bind count: 1 | |
// Lexicographical less-than comparison. | |
// lexicographical_less([0,1,2,2,4,5], [0,1,2,3,4,5]) == true | |
// lexicographical_less("012245", "012345") == true | |
// lexicographical_less("01234", "012345") == true | |
// lexicographical_less("012345", "01234") == false | |
// lexicographical_less("012345", "012345") == false | |
template <typename Container> | |
bool lexicographical_less(const Container& xs, const Container& ys) { | |
return lexicographical_less_by(is_less<typename Container::value_type>, xs, | |
ys); | |
} | |
// API search type: lexicographical_sort : [[a]] -> [[a]] | |
// fwd bind count: 0 | |
// sort by lexicographical_less | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut lexicographical_sort(Container&& xs) { | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return sort_by(lexicographical_less<T>, std::forward<Container>(xs)); | |
} | |
// API search type: replicate : (Int, a) -> [a] | |
// fwd bind count: 1 | |
// Create a sequence containing x n times. | |
// replicate(3, 1) == [1, 1, 1] | |
template <typename T, typename ContainerOut = std::vector<T>> | |
ContainerOut replicate(std::size_t n, const T& x) { | |
return ContainerOut(n, x); | |
} | |
namespace internal { | |
template <typename UnaryPredicate, typename T> | |
T instead_of_if(internal::reuse_container_t, UnaryPredicate pred, const T& alt, | |
T&& x) { | |
if (internal::invoke(pred, x)) | |
return alt; | |
else | |
return std::forward<T>(x); | |
} | |
template <typename UnaryPredicate, typename T> | |
T instead_of_if(internal::create_new_container_t, UnaryPredicate pred, | |
const T& alt, const T& x) { | |
if (internal::invoke(pred, x)) | |
return alt; | |
else | |
return x; | |
} | |
} // namespace internal | |
// API search type: instead_of_if : ((a -> Bool), a, a) -> a | |
// fwd bind count: 2 | |
// Return alt if pred(x), otherwise x itself. | |
template <typename UnaryPredicate, typename T, typename TAlt> | |
auto instead_of_if(UnaryPredicate pred, const TAlt& alt, T&& x) { | |
return internal::instead_of_if(internal::can_reuse_v<T>{}, pred, alt, | |
std::forward<T>(x)); | |
} | |
// API search type: instead_of_if_empty : ((a -> Bool), [a], [a]) -> [a] | |
// fwd bind count: 2 | |
// Return alt if xs is empty, otherwise xs itself. | |
template <typename Container, typename ContainerAlt, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut instead_of_if_empty(const ContainerAlt& alt, Container&& xs) { | |
return instead_of_if(is_empty<internal::remove_const_and_ref_t<Container>>, | |
alt, std::forward<Container>(xs)); | |
} | |
} // namespace fplus |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. | |
// https://github.com/Dobiasd/FunctionalPlus | |
// Distributed under the Boost Software License, Version 1.0. | |
// (See accompanying file LICENSE_1_0.txt or copy at | |
// http://www.boost.org/LICENSE_1_0.txt) | |
#pragma once | |
#include <fplus/compare.hpp> | |
#include <fplus/composition.hpp> | |
#include <fplus/container_traits.hpp> | |
#include <fplus/maybe.hpp> | |
#include <fplus/internal/container_common.hpp> | |
#include <fplus/internal/invoke.hpp> | |
#include <fplus/internal/meta.hpp> | |
#include <algorithm> | |
#include <cassert> | |
#include <cmath> | |
#include <cstddef> | |
#include <iterator> | |
#include <numeric> | |
namespace fplus { | |
namespace internal { | |
template <typename UnaryPredicate, typename Container> | |
void check_unary_predicate_for_container() { | |
internal::check_unary_predicate_for_type<UnaryPredicate, | |
typename Container::value_type>(); | |
} | |
template <typename F, typename Container> | |
void check_index_with_type_predicate_for_container() { | |
typedef typename Container::value_type T; | |
internal::trigger_static_asserts<internal::binary_function_tag, F, | |
std::size_t, T>(); | |
static_assert( | |
std::is_convertible<internal::invoke_result_t<F, std::size_t, T>, | |
bool>::value, | |
"Function must return bool."); | |
} | |
template <typename Compare, typename Container> | |
void check_compare_for_container() { | |
typedef typename Container::value_type T; | |
internal::trigger_static_asserts<internal::binary_predicate_tag, Compare, T, | |
T>(); | |
} | |
template <typename BinaryPredicate, typename Container> | |
void check_binary_predicate_for_container() { | |
typedef typename Container::value_type T; | |
internal::trigger_static_asserts<internal::binary_predicate_tag, | |
BinaryPredicate, T, T>(); | |
} | |
// PrepareContainer and BackInserter are overloaded | |
// to increase performance on std::vector and std::string | |
// by using std::vector<T>::reserve | |
// and std::back_inserter instead of std::back_inserter. | |
// In VC2015, release mode, Celsius W520 Xeon | |
// this leads to an increase in performance of about a factor of 3 | |
// for transform. | |
template <typename C> | |
void prepare_container( | |
const std::basic_string<C, std::char_traits<C>, std::allocator<C>> &ys, | |
std::size_t size) { | |
ys.reserve(size); | |
} | |
template <typename Y> | |
void prepare_container(std::vector<Y> &ys, std::size_t size) { | |
ys.reserve(size); | |
} | |
template <typename T, std::size_t N> | |
void prepare_container(std::array<T, N> &, std::size_t size) { | |
assert(size == N); | |
unused(size); | |
} | |
template <typename Y> | |
void prepare_container(std::unordered_set<Y> &ys, std::size_t size) { | |
ys.reserve(size); | |
} | |
template <typename Key, typename T> | |
void prepare_container(std::unordered_map<Key, T> &ys, std::size_t size) { | |
ys.reserve(size); | |
} | |
template <typename Y> | |
void prepare_container(std::unordered_multiset<Y> &ys, std::size_t size) { | |
ys.reserve(size); | |
} | |
template <typename Key, typename T> | |
void prepare_container(std::unordered_multimap<Key, T> &ys, std::size_t size) { | |
ys.reserve(size); | |
} | |
template <typename Container> | |
void prepare_container(Container &, std::size_t) {} | |
template <typename Container> | |
std::back_insert_iterator<Container> get_back_inserter(std::string &ys) { | |
return std::back_inserter(ys); | |
} | |
template <typename Container, typename Y> | |
std::back_insert_iterator<Container> get_back_inserter(std::vector<Y> &ys) { | |
return std::back_inserter(ys); | |
} | |
template <typename Container, typename Y> | |
std::back_insert_iterator<Container> get_back_inserter(std::list<Y> &ys) { | |
return std::back_inserter(ys); | |
} | |
template <typename Container, typename Y> | |
std::back_insert_iterator<Container> get_back_inserter(std::deque<Y> &ys) { | |
return std::back_inserter(ys); | |
} | |
// Avoid self-assignment. | |
template <typename T> void assign(T &x, T &&y) { | |
if (&x != &y) | |
x = std::move(y); | |
} | |
template <typename T, std::size_t N> | |
struct array_back_insert_iterator | |
: public std::back_insert_iterator<std::array<T, N>> { | |
typedef std::back_insert_iterator<std::array<T, N>> base_type; | |
explicit array_back_insert_iterator(std::array<T, N> &arr) | |
: base_type(arr), arr_ptr_(&arr), pos_(0) {} | |
array_back_insert_iterator(const array_back_insert_iterator<T, N> &other) | |
: base_type(*other.arr_ptr_), arr_ptr_(other.arr_ptr_), pos_(other.pos_) { | |
} | |
array_back_insert_iterator<T, N> & | |
operator=(const array_back_insert_iterator<T, N> &other) { | |
arr_ptr_ = other.arr_ptr_; | |
pos_ = other.pos_; | |
return *this; | |
} | |
~array_back_insert_iterator() { assert(pos_ == 0 || pos_ == N); } | |
array_back_insert_iterator<T, N> &operator=(const T &x) { | |
assert(pos_ < N); | |
(*arr_ptr_)[pos_] = x; | |
++pos_; | |
return *this; | |
} | |
array_back_insert_iterator<T, N> &operator=(T &&x) { | |
assert(pos_ < N); | |
assign((*arr_ptr_)[pos_], std::move(x)); | |
++pos_; | |
return *this; | |
} | |
array_back_insert_iterator<T, N> &operator*() { return *this; } | |
array_back_insert_iterator<T, N> &operator++() { return *this; } | |
array_back_insert_iterator<T, N> operator++(int) { return *this; } | |
private: | |
std::array<T, N> *arr_ptr_; | |
std::size_t pos_; | |
}; | |
#if defined(_MSC_VER) && _MSC_VER >= 1900 | |
template <typename T, std::size_t N> | |
struct std::_Is_checked_helper<array_back_insert_iterator<T, N>> | |
: public true_type { // mark array_back_insert_iterator as checked | |
}; | |
#endif | |
template <typename Container, typename Y, std::size_t N> | |
array_back_insert_iterator<Y, N> get_back_inserter(std::array<Y, N> &ys) { | |
return array_back_insert_iterator<Y, N>(ys); | |
} | |
template <typename Container> | |
std::insert_iterator<Container> get_back_inserter(Container &ys) { | |
return std::inserter(ys, std::end(ys)); | |
} | |
template <typename Iterator> | |
void advance_iterator(Iterator &it, std::size_t distance) { | |
std::advance(it, static_cast<typename Iterator::difference_type>(distance)); | |
} | |
template <typename T> void advance_iterator(T *&it, std::size_t distance) { | |
it += static_cast<std::ptrdiff_t>(distance); | |
} | |
template <typename Iterator> | |
Iterator add_to_iterator(Iterator it, std::size_t distance = 1) { | |
return std::next(it, | |
static_cast<typename Iterator::difference_type>(distance)); | |
} | |
// GCC 4.9 does not support std::rbegin, std::rend and | |
// std::make_reverse_iterator | |
template <typename Iterator> | |
std::reverse_iterator<Iterator> make_reverse_iterator(Iterator it) { | |
return std::reverse_iterator<Iterator>(it); | |
} | |
} // namespace internal | |
// API search type: is_even : Int -> Bool | |
// fwd bind count: 0 | |
// Checks if x is even. | |
template <typename X> bool is_even(X x) { | |
static_assert(std::is_integral<X>::value, "type must be integral"); | |
return x % 2 == 0; | |
} | |
// API search type: is_odd : Int -> Bool | |
// fwd bind count: 0 | |
// Checks if x is odd. | |
template <typename X> bool is_odd(X x) { | |
static_assert(std::is_integral<X>::value, "type must be integral"); | |
return x % 2 != 0; | |
} | |
// API search type: is_empty : [a] -> Bool | |
// fwd bind count: 0 | |
// Returns true if the container holds no elements. | |
// is_empty([1, 2]) == false | |
// is_empty([]) == true | |
template <typename Container> bool is_empty(const Container &xs) { | |
return xs.empty(); | |
} | |
// API search type: is_not_empty : [a] -> Bool | |
// fwd bind count: 0 | |
// Returns true if the container holds at least one element. | |
// is_not_empty([1, 2]) == true | |
template <typename Container> bool is_not_empty(const Container &xs) { | |
return !is_empty(xs); | |
} | |
// API search type: size_of_cont : [a] -> Int | |
// fwd bind count: 0 | |
// Returns the number of elements in the given container. | |
// size_of_cont([3, 4]) == 2 | |
template <typename Container> std::size_t size_of_cont(const Container &xs) { | |
return xs.size(); | |
} | |
// API search type: convert : a -> b | |
// fwd bind count: 0 | |
// Converts one type of element into another. | |
template <typename Dest, typename Source> Dest convert(const Source &x) { | |
return Dest(x); | |
} | |
// API search type: convert_elems : [a] -> [b] | |
// fwd bind count: 0 | |
// Converts all elements in a sequence to a different type. | |
// convert_elems<NewT>([1, 2, 3]) == [NewT(1), NewT(2), NewT(3)] | |
template <typename NewT, typename ContainerIn, | |
typename ContainerOut = | |
typename internal::same_cont_new_t<ContainerIn, NewT, 0>::type> | |
ContainerOut convert_elems(const ContainerIn &xs) { | |
static_assert( | |
std::is_constructible<NewT, typename ContainerIn::value_type>::value, | |
"Elements not convertible."); | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it = internal::get_back_inserter<ContainerOut>(ys); | |
// using 'for (const auto& x ...)' is even for ints as fast as | |
// using 'for (int x ...)' (GCC, O3), so there is no need to | |
// check if the type is fundamental and then dispatch accordingly. | |
for (const auto &x : xs) { | |
*it = convert<NewT>(x); | |
} | |
return ys; | |
} | |
// API search type: convert_container : [a] -> [a] | |
// fwd bind count: 0 | |
// Change the type of the container | |
// while keeping the elements in the sequence the same. | |
// convert_container([1, 2, 3]) == [1, 2, 3] | |
// Useful for example if you want to convert an std::list to an std::vector. | |
template <typename ContainerOut, typename ContainerIn> | |
ContainerOut convert_container(const ContainerIn &xs) { | |
typedef typename ContainerIn::value_type SourceElem; | |
typedef typename ContainerOut::value_type DestElem; | |
static_assert(std::is_same<DestElem, SourceElem>::value, | |
"Source and dest container must have the same value_type"); | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto itOut = internal::get_back_inserter<ContainerOut>(ys); | |
std::copy(std::begin(xs), std::end(xs), itOut); | |
return ys; | |
} | |
// API search type: convert_container_and_elems : [a] -> [b] | |
// fwd bind count: 0 | |
// Converts between different containers and elements. | |
// Dest elements are allowed to have explicit constructors. | |
// convert([1, 2, 3]) == [1, 2, 3] | |
template <typename ContainerOut, typename ContainerIn> | |
ContainerOut convert_container_and_elems(const ContainerIn &xs) { | |
static_assert(std::is_convertible<typename ContainerIn::value_type, | |
typename ContainerOut::value_type>::value, | |
"Elements not convertible."); | |
typedef typename ContainerOut::value_type DestElem; | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it = internal::get_back_inserter<ContainerOut>(ys); | |
for (const auto &x : xs) { | |
*it = convert<DestElem>(x); | |
} | |
return ys; | |
} | |
namespace internal { | |
template <typename Container> | |
Container get_segment(internal::reuse_container_t, std::size_t idx_begin, | |
std::size_t idx_end, Container &&xs) { | |
idx_end = std::min(idx_end, size_of_cont(xs)); | |
if (idx_end <= idx_begin) { | |
xs.clear(); | |
return std::forward<Container>(xs); | |
} | |
auto itBegin = std::begin(xs); | |
internal::advance_iterator(itBegin, idx_begin); | |
auto itEnd = itBegin; | |
internal::advance_iterator(itEnd, idx_end - idx_begin); | |
xs.erase(std::copy(itBegin, itEnd, std::begin(xs)), std::end(xs)); | |
return std::forward<Container>(xs); | |
} | |
template <typename Container> | |
Container get_segment(internal::create_new_container_t, std::size_t idx_begin, | |
std::size_t idx_end, const Container &xs) { | |
idx_end = std::min(idx_end, size_of_cont(xs)); | |
if (idx_end <= idx_begin) { | |
return {}; | |
} | |
Container result; | |
auto itBegin = std::begin(xs); | |
internal::advance_iterator(itBegin, idx_begin); | |
auto itEnd = itBegin; | |
internal::advance_iterator(itEnd, idx_end - idx_begin); | |
std::copy(itBegin, itEnd, internal::get_back_inserter(result)); | |
return result; | |
} | |
} // namespace internal | |
// API search type: get_segment : (Int, Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Return a defined segment from the sequence. | |
// get_segment(2, 5, [0,1,2,3,4,5,6,7,8]) == [2,3,4] | |
// get_segment(2, 15, [0,1,2,3,4,5,6,7,8]) == [2,3,4,5,6,7,8] | |
// get_segment(5, 2, [0,1,2,3,4,5,6,7,8]) == [] | |
// Also known as slice. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut get_segment(std::size_t idx_begin, std::size_t idx_end, | |
Container &&xs) { | |
return internal::get_segment(internal::can_reuse_v<Container>{}, idx_begin, | |
idx_end, std::forward<Container>(xs)); | |
} | |
namespace internal { | |
template <typename ContainerToken, typename Container> | |
Container set_segment(internal::reuse_container_t, std::size_t idx_begin, | |
const ContainerToken &token, Container &&xs) { | |
assert(idx_begin + size_of_cont(token) < size_of_cont(xs)); | |
auto itBegin = std::begin(xs); | |
internal::advance_iterator(itBegin, idx_begin); | |
std::copy(std::begin(token), std::end(token), itBegin); | |
return std::forward<Container>(xs); | |
} | |
template <typename ContainerToken, typename Container> | |
Container set_segment(internal::create_new_container_t, std::size_t idx_begin, | |
const ContainerToken &token, const Container &xs) { | |
Container result = xs; | |
return set_segment(internal::reuse_container_t(), idx_begin, token, | |
std::move(result)); | |
} | |
} // namespace internal | |
// API search type: set_segment : (Int, [a], [a]) -> [a] | |
// fwd bind count: 2 | |
// Replace part of a sequence with a token. | |
// set_segment(2, [9,9,9], [0,1,2,3,4,5,6,7,8]) == [0,1,9,9,9,5,6,7,8] | |
// Crashes on invalid indices. | |
// Also known as replace_segment. | |
template <typename ContainerToken, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut set_segment(std::size_t idx_begin, const ContainerToken &token, | |
Container &&xs) { | |
return internal::set_segment(internal::can_reuse_v<Container>{}, idx_begin, | |
token, std::forward<Container>(xs)); | |
} | |
namespace internal { | |
template <typename Container> | |
Container remove_segment(internal::reuse_container_t, std::size_t idx_begin, | |
std::size_t idx_end, Container &&xs) { | |
assert(idx_begin <= idx_end); | |
assert(idx_end <= size_of_cont(xs)); | |
auto firstBreakIt = std::begin(xs); | |
internal::advance_iterator(firstBreakIt, idx_begin); | |
auto secondBreakIt = std::begin(xs); | |
internal::advance_iterator(secondBreakIt, idx_end); | |
xs.erase(std::copy(secondBreakIt, std::end(xs), firstBreakIt), std::end(xs)); | |
return std::forward<Container>(xs); | |
} | |
template <typename Container> | |
Container remove_segment(internal::create_new_container_t, | |
std::size_t idx_begin, std::size_t idx_end, | |
const Container &xs) { | |
assert(idx_begin <= idx_end); | |
assert(idx_end <= size_of_cont(xs)); | |
Container result; | |
std::size_t length = idx_end - idx_begin; | |
internal::prepare_container(result, size_of_cont(xs) - length); | |
auto firstBreakIt = std::begin(xs); | |
internal::advance_iterator(firstBreakIt, idx_begin); | |
std::copy(std::begin(xs), firstBreakIt, internal::get_back_inserter(result)); | |
auto secondBreakIt = std::begin(xs); | |
internal::advance_iterator(secondBreakIt, idx_end); | |
std::copy(secondBreakIt, std::end(xs), internal::get_back_inserter(result)); | |
return result; | |
} | |
} // namespace internal | |
// API search type: remove_segment : (Int, Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Cuts our a defined segment from the sequence. | |
// remove_segment(2, 5, [0,1,2,3,4,5,6,7]) == [0,1,5,6,7] | |
// crashes on invalid indices | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut remove_segment(std::size_t idx_begin, std::size_t idx_end, | |
Container &&xs) { | |
return internal::remove_segment(internal::can_reuse_v<Container>{}, idx_begin, | |
idx_end, std::forward<Container>(xs)); | |
} | |
// API search type: insert_at : (Int, [a], [a]) -> [a] | |
// fwd bind count: 2 | |
// Inserts a token into a sequence at a specific position. | |
// insert_at(2, [8,9], [0,1,2,3,4]) == [0,1,8,9,2,3,4] | |
// Unsafe! Crashes on invalid index. | |
template <typename Container> | |
Container insert_at(std::size_t idx_begin, const Container &token, | |
const Container &xs) { | |
assert(idx_begin <= size_of_cont(xs)); | |
Container result; | |
internal::prepare_container(result, size_of_cont(xs) + size_of_cont(token)); | |
auto breakIt = std::begin(xs); | |
internal::advance_iterator(breakIt, idx_begin); | |
std::copy(std::begin(xs), breakIt, internal::get_back_inserter(result)); | |
std::copy(std::begin(token), std::end(token), | |
internal::get_back_inserter(result)); | |
std::copy(breakIt, std::end(xs), internal::get_back_inserter(result)); | |
return result; | |
} | |
// API search type: elem_at_idx : (Int, [a]) -> a | |
// fwd bind count: 1 | |
// Return the nth element of a sequence. | |
// elem_at_idx(2, [7,6,5,4,3]) == 5 | |
// Unsafe! Crashes on invalid index. | |
template <typename Container> | |
auto elem_at_idx(std::size_t idx, const Container &xs) { | |
assert(idx < size_of_cont(xs)); | |
auto it = std::begin(xs); | |
internal::advance_iterator(it, idx); | |
return *it; | |
} | |
// API search type: elem_at_idx_maybe : (Int, [a]) -> Maybe a | |
// fwd bind count: 1 | |
// Return the nth element of a sequence if existing. | |
// elem_at_idx_maybe(2, [7,6,5,4,3]) == Just 5 | |
// elem_at_idx_maybe(9, [7,6,5,4,3]) == Nothing | |
// Use elem_at_idx_or_nothing if you want to provide a signed index type. | |
template <typename Container, typename T = typename Container::value_type> | |
maybe<T> elem_at_idx_maybe(std::size_t idx, const Container &xs) { | |
if (size_of_cont(xs) < idx) { | |
return {}; | |
} | |
auto it = std::begin(xs); | |
internal::advance_iterator(it, idx); | |
return *it; | |
} | |
// API search type: elems_at_idxs : ([Int], [a]) -> [a] | |
// fwd bind count: 1 | |
// Construct a subsequence from the elements with the given indices. | |
// elem_at_idxs([1, 3], [7,6,5,4,3]) == [6, 4] | |
template <typename Container, typename ContainerIdxs, | |
typename T = typename Container::value_type, | |
typename ContainerOut = std::vector<T>> | |
std::vector<T> elems_at_idxs(const ContainerIdxs &idxs, const Container &xs) { | |
static_assert( | |
std::is_same<typename ContainerIdxs::value_type, std::size_t>::value, | |
"Indices must be std::size_t"); | |
ContainerOut result; | |
internal::prepare_container(result, size_of_cont(idxs)); | |
auto itOut = internal::get_back_inserter(result); | |
for (std::size_t idx : idxs) { | |
*itOut = elem_at_idx(idx, xs); | |
} | |
return result; | |
} | |
namespace internal { | |
template <typename Container, typename F> | |
Container transform(internal::reuse_container_t, F f, Container &&xs) { | |
internal::trigger_static_asserts<internal::unary_function_tag, F, | |
decltype(*std::begin(xs))>(); | |
std::transform(std::begin(xs), std::end(xs), std::begin(xs), f); | |
return std::forward<Container>(xs); | |
} | |
template <typename ContainerOut, typename F, typename ContainerIn> | |
ContainerOut transform(internal::create_new_container_t, F f, | |
const ContainerIn &xs) { | |
internal::trigger_static_asserts<internal::unary_function_tag, F, | |
decltype(*std::begin(xs))>(); | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it = internal::get_back_inserter<ContainerOut>(ys); | |
std::transform(std::begin(xs), std::end(xs), it, f); | |
return ys; | |
} | |
} // namespace internal | |
// API search type: transform : ((a -> b), [a]) -> [b] | |
// fwd bind count: 1 | |
// Apply a function to every element in a sequence. | |
// transform((*2), [1, 3, 4]) == [2, 6, 8] | |
// Also known as map or fmap. | |
template < | |
typename F, typename ContainerIn, | |
typename ContainerOut = typename internal::same_cont_new_t_from_unary_f< | |
internal::remove_const_and_ref_t<ContainerIn>, F, 0>::type> | |
ContainerOut transform(F f, ContainerIn &&xs) { | |
using reuse_t = typename std::conditional< | |
std::is_same<internal::can_reuse_v<ContainerIn>, | |
internal::reuse_container_t>::value && | |
std::is_base_of<std::true_type, | |
internal::has_order<ContainerIn>>::value && | |
std::is_same<internal::remove_const_and_ref_t<ContainerIn>, | |
ContainerOut>::value, | |
internal::reuse_container_t, internal::create_new_container_t>::type; | |
return internal::transform<ContainerOut>(reuse_t{}, f, | |
std::forward<ContainerIn>(xs)); | |
} | |
// API search type: transform_convert : ((a -> b), [a]) -> [b] | |
// fwd bind count: 1 | |
// transform_convert((*2), [1, 3, 4]) == [2, 6, 8] | |
// Same as transform, but makes it easy to | |
// use an output container type different from the input container type. | |
template <typename ContainerOut, typename F, typename ContainerIn> | |
ContainerOut transform_convert(F f, const ContainerIn &xs) { | |
internal::trigger_static_asserts<internal::unary_function_tag, F, | |
typename ContainerIn::value_type>(); | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it = internal::get_back_inserter<ContainerOut>(ys); | |
std::transform(std::begin(xs), std::end(xs), it, f); | |
return ys; | |
} | |
// API search type: transform_inner : ((a -> b), [[a]]) -> [[b]] | |
// fwd bind count: 1 | |
// Applies a function to the elements of the inner containers | |
// of a nested sequence. | |
// transform_inner((*2), [[1, 3, 4], [1, 2]]) == [[2, 6, 8], [2, 4]] | |
// Also known as transform_nested, map_nested or map_inner. | |
template <typename F, typename ContainerIn, | |
typename ContainerOut = typename internal::same_cont_new_t< | |
ContainerIn, | |
typename internal::same_cont_new_t_from_unary_f< | |
typename ContainerIn::value_type, F, 0>::type, | |
0>::type> | |
ContainerOut transform_inner(F f, const ContainerIn &xs) { | |
internal::trigger_static_asserts< | |
internal::unary_function_tag, F, | |
typename ContainerIn::value_type::value_type>(); | |
return fplus::transform( | |
fplus::bind_1st_of_2( | |
fplus::transform<F, const typename ContainerIn::value_type &>, f), | |
xs); | |
} | |
namespace internal { | |
template <typename Container> | |
Container reverse(internal::reuse_container_t, Container &&xs) { | |
static_assert(internal::has_order<Container>::value, | |
"Reverse: Container has no order."); | |
std::reverse(std::begin(xs), std::end(xs)); | |
return std::forward<Container>(xs); | |
} | |
template <typename Container> | |
Container reverse(internal::create_new_container_t, const Container &xs) { | |
static_assert(internal::has_order<Container>::value, | |
"Reverse: Container has no order."); | |
Container ys = xs; | |
std::reverse(std::begin(ys), std::end(ys)); | |
return ys; | |
} | |
} // namespace internal | |
// API search type: reverse : [a] -> [a] | |
// fwd bind count: 0 | |
// Reverse a sequence. | |
// reverse([0,4,2,6]) == [6,2,4,0] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut reverse(Container &&xs) { | |
return internal::reverse(internal::can_reuse_v<Container>{}, | |
std::forward<Container>(xs)); | |
} | |
// API search type: take : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Return the first n elements of a sequence xs. | |
// In case n >= length(xs), xs is returned. | |
// take(3, [0,1,2,3,4,5,6,7]) == [0,1,2] | |
// take(10, [0,1,2]) == [0,1,2] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut take(std::size_t amount, Container &&xs) { | |
if (amount >= size_of_cont(xs)) | |
return xs; | |
return get_segment(0, amount, std::forward<Container>(xs)); | |
} | |
// API search type: take_exact : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Return exactly the first n elements of a sequence xs. | |
// Unsafe! Crashes then sequence is too short. | |
// take_exact(3, [0,1,2,3,4,5,6,7]) == [0,1,2] | |
// take_exact(10, [0,1,2]) == crash | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut take_exact(std::size_t amount, Container &&xs) { | |
return get_segment(0, amount, std::forward<Container>(xs)); | |
} | |
// API search type: take_cyclic : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Takes n elements from a sequence considering it as cyclic. | |
// take_cyclic(5, [0,1,2,3]) == [0,1,2,3,0] | |
// take_cyclic(7, [0,1,2,3]) == [0,1,2,3,0,1,2] | |
// take_cyclic(7, [0,1]) == [0,1,0,1,0,1,0] | |
// take_cyclic(2, [0,1,2,3]) == [0,1] | |
// take_cyclic(3, [0]) == [0,0,0] | |
// take_cyclic(3, []) == crash! | |
// Also known as take_wrap. | |
// xs must be non-empty. | |
template <typename Container> | |
Container take_cyclic(std::size_t amount, const Container &xs) { | |
assert(!xs.empty()); | |
Container ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it_out = internal::get_back_inserter(ys); | |
auto it_in = std::begin(xs); | |
while (amount != 0) { | |
*it_out = *it_in; | |
--amount; | |
++it_in; | |
if (it_in == std::end(xs)) { | |
it_in = std::begin(xs); | |
} | |
} | |
return ys; | |
} | |
// API search type: drop : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Skip the first n elements of a sequence xs. | |
// If n > length(xs) an empty sequence is returned. | |
// drop(3, [0,1,2,3,4,5,6,7]) == [3,4,5,6,7] | |
// Also known as skip. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut drop(std::size_t amount, Container &&xs) { | |
if (amount >= size_of_cont(xs)) | |
return ContainerOut(); | |
return get_segment(amount, size_of_cont(xs), std::forward<Container>(xs)); | |
} | |
// API search type: take_last : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Return the last n elements of a sequence xs. | |
// In case n >= length(xs), xs is returned. | |
// take_last(3, [0,1,2,3,4,5,6,7]) == [5,6,7] | |
// take_last(10, [0,1,2]) == [0,1,2] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut take_last(std::size_t amount, Container &&xs) { | |
if (amount >= size_of_cont(xs)) | |
return xs; | |
return drop(size_of_cont(xs) - amount, std::forward<Container>(xs)); | |
} | |
// API search type: drop_last : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Skip the last n elements of a sequence xs. | |
// If n > length(xs) an empty sequence is returned. | |
// drop_last(3, [0,1,2,3,4,5,6,7]) == [0,1,2,3,4] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut drop_last(std::size_t amount, Container &&xs) { | |
if (amount >= size_of_cont(xs)) | |
return ContainerOut(); | |
return take(size_of_cont(xs) - amount, std::forward<Container>(xs)); | |
} | |
// API search type: drop_exact : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Skip exactly the first n elements of a sequence xs. | |
// Unsafe! Crashes when xs is too short. | |
// drop_exact(3, [0,1,2,3,4,5,6,7]) == [3,4,5,6,7] | |
// drop_exact(10, [0,1,2,3,4,5,6,7]) == crash | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut drop_exact(std::size_t amount, Container &&xs) { | |
return get_segment(amount, size_of_cont(xs), std::forward<Container>(xs)); | |
} | |
// API search type: take_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Take elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// take_while(is_even, [0,2,4,5,6,7,8]) == [0,2,4] | |
template <typename Container, typename UnaryPredicate> | |
Container take_while(UnaryPredicate pred, const Container &xs) { | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
auto itFirst = std::find_if(std::begin(xs), std::end(xs), logical_not(pred)); | |
if (itFirst == std::end(xs)) | |
return xs; | |
return Container(std::begin(xs), itFirst); | |
} | |
// API search type: take_last_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Take elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// take_last_while(is_even, [0,2,7,5,6,4,8]) == [6,4,8] | |
template <typename Container, typename UnaryPredicate> | |
Container take_last_while(UnaryPredicate pred, const Container &xs) { | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
const auto r_begin = internal::make_reverse_iterator(std::end(xs)); | |
const auto r_end = internal::make_reverse_iterator(std::begin(xs)); | |
const auto itFirstReverse = std::find_if(r_begin, r_end, logical_not(pred)); | |
if (itFirstReverse == r_begin) | |
return Container(); | |
if (itFirstReverse == r_end) | |
return xs; | |
return Container(itFirstReverse.base(), std::end(xs)); | |
} | |
// API search type: drop_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Remove elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// drop_while(is_even, [0,2,4,5,6,7,8]) == [5,6,7,8] | |
// Also known as trim_left_by. | |
template <typename Container, typename UnaryPredicate> | |
Container drop_while(UnaryPredicate pred, const Container &xs) { | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
auto itFirstNot = std::find_if_not(std::begin(xs), std::end(xs), pred); | |
if (itFirstNot == std::end(xs)) | |
return Container(); | |
return Container(itFirstNot, std::end(xs)); | |
} | |
// API search type: drop_last_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Remove elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// drop_last_while(is_even, [0,2,7,5,6,4,8]) == [0,2,7,5] | |
template <typename Container, typename UnaryPredicate> | |
Container drop_last_while(UnaryPredicate pred, const Container &xs) { | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
const auto r_begin = internal::make_reverse_iterator(std::end(xs)); | |
const auto r_end = internal::make_reverse_iterator(std::begin(xs)); | |
const auto itFirstNotReverse = std::find_if_not(r_begin, r_end, pred); | |
if (itFirstNotReverse == r_begin) | |
return xs; | |
if (itFirstNotReverse == r_end) | |
return Container(); | |
return Container(std::begin(xs), itFirstNotReverse.base()); | |
} | |
// API search type: fold_left : (((a, b) -> a), a, [b]) -> a | |
// fwd bind count: 2 | |
// fold_left((+), 0, [1, 2, 3]) == ((0+1)+2)+3 == 6 | |
// Takes the second argument and the first item of the list | |
// and applies the function to them, | |
// then feeds the function with this result and the second argument and so on. | |
template <typename F, typename Container, typename Acc> | |
Acc fold_left(F f, const Acc &init, const Container &xs) { | |
using std::begin; | |
using std::end; | |
return internal::accumulate(begin(xs), end(xs), init, f); | |
} | |
// API search type: reduce : (((a, a) -> a), a, [a]) -> a | |
// fwd bind count: 2 | |
// reduce((+), 0, [1, 2, 3]) == (0+1+2+3) == 6 | |
// Combines the initial value and all elements of the sequence | |
// using the given function. | |
// The set of f, init and value_type should form a monoid. | |
template <typename F, typename Container> | |
typename Container::value_type | |
reduce(F f, const typename Container::value_type &init, const Container &xs) { | |
return fold_left(f, init, xs); | |
} | |
// API search type: fold_left_1 : (((a, a) -> a), [a]) -> a | |
// fwd bind count: 1 | |
// fold_left_1((+), [1, 2, 3]) == (1+2)+3 == 6 | |
// Takes the first 2 items of the list and applies the function to them, | |
// then feeds the function with this result and the third argument and so on. | |
// xs must be non-empty. | |
template <typename F, typename Container> | |
auto fold_left_1(F f, const Container &xs) { | |
assert(!xs.empty()); | |
using std::begin; | |
using std::end; | |
const auto it = begin(xs); | |
return internal::accumulate(std::next(it), end(xs), *it, f); | |
} | |
// API search type: reduce_1 : (((a, a) -> a), [a]) -> a | |
// fwd bind count: 1 | |
// reduce_1((+), [1, 2, 3]) == (1+2+3) == 6 | |
// Joins all elements of the sequence using the given function. | |
// The set of f and value_type should form a semigroup. | |
template <typename F, typename Container> | |
typename Container::value_type reduce_1(F f, const Container &xs) { | |
assert(!xs.empty()); | |
return fold_left_1(f, xs); | |
} | |
// API search type: fold_right : (((a, b) -> b), b) -> [a] -> b | |
// fwd bind count: 2 | |
// fold_right((+), 0, [1, 2, 3]) == 1+(2+(3+0)) == 6 | |
// Takes the second argument and the last item of the list | |
// and applies the function, | |
// then it takes the penultimate item from the end and the result, and so on. | |
template <typename F, typename Container, typename Acc> | |
Acc fold_right(F f, const Acc &init, const Container &xs) { | |
return internal::accumulate(xs.rbegin(), xs.rend(), init, flip(f)); | |
} | |
// API search type: fold_right_1 : (((a, a) -> a), [a]) -> a | |
// fwd bind count: 1 | |
// fold_right_1((+), [1, 2, 3]) == 1+(2+3)) == 6 | |
// Takes the last two items of the list and applies the function, | |
// then it takes the third item from the end and the result, and so on. | |
template <typename F, typename Container> | |
auto fold_right_1(F f, const Container &xs) { | |
assert(!xs.empty()); | |
const auto it = xs.rbegin(); | |
return internal::accumulate(std::next(it), xs.rend(), *it, flip(f)); | |
} | |
// API search type: scan_left : (((a, b) -> a), a, [b]) -> [a] | |
// fwd bind count: 2 | |
// scan_left((+), 0, [1, 2, 3]) == [0, 1, 3, 6] | |
// Takes the second argument and the first item of the list | |
// and applies the function to them, | |
// then feeds the function with this result and the second argument and so on. | |
// It returns the list of intermediate and final results. | |
template <typename F, typename ContainerIn, typename Acc> | |
auto scan_left(F f, const Acc &init, const ContainerIn &xs) { | |
using ContainerOut = | |
typename internal::same_cont_new_t<ContainerIn, Acc, 1>::type; | |
ContainerOut result; | |
internal::prepare_container(result, size_of_cont(xs) + 1); | |
using std::begin; | |
using std::end; | |
internal::scan_impl(f, init, internal::get_back_inserter(result), begin(xs), | |
end(xs)); | |
return result; | |
} | |
// API search type: scan_left_1 : (((a, a) -> a), [a]) -> [a] | |
// fwd bind count: 1 | |
// scan_left_1((+), [1, 2, 3]) == [1, 3, 6] | |
// Takes the first 2 items of the list and applies the function to them, | |
// then feeds the function with this result and the third argument and so on. | |
// It returns the list of intermediate and final results. | |
// xs must be non-empty. | |
template <typename F, typename ContainerIn> | |
auto scan_left_1(F f, const ContainerIn &xs) { | |
assert(!xs.empty()); | |
using std::begin; | |
using std::end; | |
const auto beginIt = begin(xs); | |
using ContainerOut = typename internal::same_cont_new_t< | |
ContainerIn, internal::uncvref_t<decltype(*beginIt)>, 0>::type; | |
ContainerOut result; | |
internal::prepare_container(result, size_of_cont(xs)); | |
internal::scan_impl(f, *beginIt, internal::get_back_inserter(result), | |
std::next(beginIt), end(xs)); | |
return result; | |
} | |
// API search type: scan_right : (((a, b) -> b), b, [a]) -> [b] | |
// fwd bind count: 2 | |
// scan_right((+), 0, [1, 2, 3]) == [6, 5, 3, 0] | |
// Takes the second argument and the last item of the list | |
// and applies the function, | |
// then it takes the penultimate item from the end and the result, and so on. | |
// It returns the list of intermediate and final results. | |
template < | |
typename F, typename ContainerIn, | |
typename Acc = typename utils::function_traits<F>::template arg<1>::type, | |
typename ContainerOut = | |
typename internal::same_cont_new_t<ContainerIn, Acc, 1>::type> | |
ContainerOut scan_right(F f, const Acc &init, const ContainerIn &xs) { | |
return reverse(scan_left(flip(f), init, reverse(xs))); | |
} | |
// API search type: scan_right_1 : (((a, a) -> a), [a]) -> [a] | |
// fwd bind count: 1 | |
// scan_right_1((+), [1, 2, 3]) == [6, 5, 3] | |
// Takes the last two items of the list and applies the function, | |
// then it takes the third item from the end and the result, and so on. | |
// It returns the list of inntermediate and final results. | |
template <typename F, typename ContainerIn, | |
typename Acc = typename ContainerIn::value_type, | |
typename ContainerOut = | |
typename internal::same_cont_new_t<ContainerIn, Acc, 0>::type> | |
ContainerOut scan_right_1(F f, const ContainerIn &xs) { | |
return reverse(scan_left_1(flip(f), reverse(xs))); | |
} | |
// API search type: sum : [a] -> a | |
// fwd bind count: 0 | |
// Adds up all values in a sequence. | |
// sum([0,3,1]) == 4 | |
// sum([]) == 0 | |
template <typename Container, typename T = typename Container::value_type> | |
T sum(const Container &xs) { | |
T result = T(); | |
for (const auto &x : xs) { | |
result = result + x; | |
} | |
return result; | |
} | |
// API search type: product : [a] -> a | |
// fwd bind count: 0 | |
// Returns the product of all values in a sequence. | |
// product([3,1,2]) == 6 | |
// product([]) == 1 | |
template <typename Container, typename T = typename Container::value_type> | |
T product(const Container &xs) { | |
T result{1}; | |
for (const auto &x : xs) { | |
result = result * x; | |
} | |
return result; | |
} | |
namespace internal { | |
template <typename T, typename Container> | |
Container append_elem(internal::reuse_container_t, const T &y, Container &&xs) { | |
*internal::get_back_inserter(xs) = y; | |
return std::forward<Container>(xs); | |
} | |
template <typename T, typename Container> | |
Container append_elem(internal::create_new_container_t, const T &y, | |
const Container &xs) { | |
Container result; | |
internal::prepare_container(result, size_of_cont(xs) + 1); | |
std::copy(std::begin(xs), std::end(xs), internal::get_back_inserter(result)); | |
*internal::get_back_inserter(result) = y; | |
return result; | |
} | |
} // namespace internal | |
// API search type: append_elem : (a, [a]) -> [a] | |
// fwd bind count: 1 | |
// Extends a sequence with one element at the back. | |
// append_elem([1, 2], 3) == [1, 2, 3] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>, | |
typename T = typename ContainerOut::value_type> | |
ContainerOut append_elem(const T &y, Container &&xs) { | |
return internal::append_elem(internal::can_reuse_v<Container>{}, y, | |
std::forward<Container>(xs)); | |
} | |
namespace internal { | |
template <typename T> | |
std::list<T> prepend_elem(internal::reuse_container_t, const T &y, | |
std::list<T> &&xs) { | |
xs.push_front(y); | |
return std::forward<std::list<T>>(xs); | |
} | |
template <typename T, typename Container> | |
Container prepend_elem(internal::reuse_container_t, const T &y, | |
Container &&xs) { | |
xs.resize(size_of_cont(xs) + 1); | |
std::copy(++xs.rbegin(), xs.rend(), xs.rbegin()); | |
*std::begin(xs) = y; | |
return std::forward<Container>(xs); | |
} | |
template <typename T, typename Container> | |
Container prepend_elem(internal::create_new_container_t, const T &y, | |
const Container &xs) { | |
Container result; | |
internal::prepare_container(result, size_of_cont(xs) + 1); | |
*internal::get_back_inserter(result) = y; | |
std::copy(std::begin(xs), std::end(xs), internal::get_back_inserter(result)); | |
return result; | |
} | |
} // namespace internal | |
// API search type: prepend_elem : (a, [a]) -> [a] | |
// fwd bind count: 1 | |
// Extends a sequence with one element in the front. | |
// prepend_elem([2, 3], 1) == [1, 2, 3] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>, | |
typename T = typename ContainerOut::value_type> | |
ContainerOut prepend_elem(const T &y, Container &&xs) { | |
return internal::prepend_elem(internal::can_reuse_v<Container>{}, y, | |
std::forward<Container>(xs)); | |
} | |
// API search type: append : ([a], [a]) -> [a] | |
// fwd bind count: 1 | |
// Concatenates two sequences. | |
// append([1, 2], [3, 4, 5]) == [1, 2, 3, 4, 5] | |
template <typename ContainerIn1, typename ContainerIn2 = ContainerIn1, | |
typename ContainerOut = ContainerIn1> | |
ContainerOut append(const ContainerIn1 &xs, const ContainerIn2 &ys) { | |
ContainerOut result; | |
internal::prepare_container(result, size_of_cont(xs) + size_of_cont(ys)); | |
std::copy(std::begin(xs), std::end(xs), internal::get_back_inserter(result)); | |
std::copy(std::begin(ys), std::end(ys), internal::get_back_inserter(result)); | |
return result; | |
} | |
// API search type: append_convert : ([a], [a]) -> [a] | |
// fwd bind count: 1 | |
// Same as append, but makes it easier to | |
// use an output container type different from the input container type. | |
template <typename ContainerOut, typename ContainerIn1, | |
typename ContainerIn2 = ContainerIn1> | |
ContainerOut append_convert(const ContainerIn1 &xs, const ContainerIn2 &ys) { | |
return append<ContainerIn1, ContainerIn2, ContainerOut>(xs, ys); | |
} | |
// API search type: concat : [[a]] -> [a] | |
// fwd bind count: 0 | |
// Concatenates multiple sequences. | |
// concat([[1, 2], [], [3]]) == [1, 2, 3] | |
// Also known as flatten. | |
template <typename ContainerIn, | |
typename ContainerOut = typename ContainerIn::value_type> | |
ContainerOut concat(const ContainerIn &xss) { | |
std::size_t length = | |
sum(transform(size_of_cont<typename ContainerIn::value_type>, xss)); | |
ContainerOut result; | |
internal::prepare_container(result, length); | |
using std::begin; | |
using std::end; | |
for (const auto &xs : xss) { | |
result.insert(end(result), begin(xs), end(xs)); | |
} | |
return result; | |
} | |
// API search type: interweave : ([a], [a]) -> [a] | |
// fwd bind count: 1 | |
// Return a sequence that contains elements from the two provided sequences | |
// in alternating order. If one list runs out of items, | |
// appends the items from the remaining list. | |
// interweave([1,3], [2,4]) == [1,2,3,4] | |
// interweave([1,3,5,7], [2,4]) == [1,2,3,4,5,7] | |
// See interleave for interweaving more than two sequences. | |
template <typename Container> | |
Container interweave(const Container &xs, const Container &ys) { | |
Container result; | |
internal::prepare_container(result, size_of_cont(xs) + size_of_cont(ys)); | |
auto it = internal::get_back_inserter<Container>(result); | |
auto it_xs = std::begin(xs); | |
auto it_ys = std::begin(ys); | |
while (it_xs != std::end(xs) || it_ys != std::end(ys)) { | |
if (it_xs != std::end(xs)) | |
*it = *(it_xs++); | |
if (it_ys != std::end(ys)) | |
*it = *(it_ys++); | |
} | |
return result; | |
} | |
// API search type: unweave : [a] -> ([a], [a]) | |
// fwd bind count: 0 | |
// Puts the elements with an even index into the first list, | |
// and the elements with an odd index into the second list. | |
// Inverse of interweave. | |
// unweave([0,1,2,3]) == ([0,2], [1,3]) | |
// unweave([0,1,2,3,4]) == ([0,2,4], [1,3]) | |
template <typename Container> | |
std::pair<Container, Container> unweave(const Container &xs) { | |
std::pair<Container, Container> result; | |
if (is_even(size_of_cont(xs))) | |
internal::prepare_container(result.first, size_of_cont(xs) / 2); | |
else | |
internal::prepare_container(result.first, size_of_cont(xs) / 2 + 1); | |
internal::prepare_container(result.second, size_of_cont(xs) / 2); | |
auto it_even = internal::get_back_inserter<Container>(result.first); | |
auto it_odd = internal::get_back_inserter<Container>(result.second); | |
std::size_t counter = 0; | |
for (const auto &x : xs) { | |
if (counter++ % 2 == 0) | |
*it_even = x; | |
else | |
*it_odd = x; | |
} | |
return result; | |
} | |
namespace internal { | |
template <typename Compare, typename T> | |
std::list<T> sort_by(internal::reuse_container_t, Compare comp, | |
std::list<T> &&xs) { | |
xs.sort(comp); | |
return std::forward<std::list<T>>(xs); | |
} | |
template <typename Compare, typename T> | |
std::list<T> sort_by(internal::create_new_container_t, Compare comp, | |
const std::list<T> &xs) { | |
auto result = xs; | |
result.sort(comp); | |
return result; | |
} | |
template <typename Compare, typename Container> | |
Container sort_by(internal::reuse_container_t, Compare comp, Container &&xs) { | |
std::sort(std::begin(xs), std::end(xs), comp); | |
return std::forward<Container>(xs); | |
} | |
template <typename Compare, typename Container> | |
Container sort_by(internal::create_new_container_t, Compare comp, | |
const Container &xs) { | |
auto result = xs; | |
std::sort(std::begin(result), std::end(result), comp); | |
return result; | |
} | |
} // namespace internal | |
// API search type: sort_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence by given less comparator. | |
template <typename Compare, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut sort_by(Compare comp, Container &&xs) { | |
return internal::sort_by(internal::can_reuse_v<Container>{}, comp, | |
std::forward<Container>(xs)); | |
} | |
namespace internal { | |
// workarounds for clang bug 24115 | |
// (std::sort and std::unique with std::function as comp) | |
// https://llvm.org/bugs/show_bug.cgi?id=24115 | |
template <typename F> struct is_less_by_struct { | |
is_less_by_struct(F f) : f_(f){}; | |
template <typename T> bool operator()(const T &x, const T &y) { | |
return f_(x) < f_(y); | |
} | |
private: | |
F f_; | |
}; | |
template <typename F> struct is_equal_by_struct { | |
is_equal_by_struct(F f) : f_(f){}; | |
template <typename T> bool operator()(const T &x, const T &y) { | |
return f_(x) == f_(y); | |
} | |
private: | |
F f_; | |
}; | |
} // namespace internal | |
// API search type: sort_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence by a given transformer. | |
template <typename F, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut sort_on(F f, Container &&xs) { | |
return sort_by(internal::is_less_by_struct<F>(f), | |
std::forward<Container>(xs)); | |
} | |
// API search type: sort : [a] -> [a] | |
// fwd bind count: 0 | |
// Sort a sequence to ascending order using std::less. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut sort(Container &&xs) { | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return sort_by(std::less<T>(), std::forward<Container>(xs)); | |
} | |
namespace internal { | |
template <typename Compare, typename T> | |
std::list<T> stable_sort_by(internal::reuse_container_t, Compare comp, | |
std::list<T> &&xs) { | |
xs.sort(comp); // std::list<T>::sort ist already stable. | |
return std::forward<std::list<T>>(xs); | |
} | |
template <typename Compare, typename T> | |
std::list<T> stable_sort_by(internal::create_new_container_t, Compare comp, | |
const std::list<T> &xs) { | |
auto result = xs; | |
result.sort(comp); // std::list<T>::sort ist already stable. | |
return result; | |
} | |
template <typename Compare, typename Container> | |
Container stable_sort_by(internal::reuse_container_t, Compare comp, | |
Container &&xs) { | |
std::sort(std::begin(xs), std::end(xs), comp); | |
return std::forward<Container>(xs); | |
} | |
template <typename Compare, typename Container> | |
Container stable_sort_by(internal::create_new_container_t, Compare comp, | |
const Container &xs) { | |
auto result = xs; | |
std::sort(std::begin(result), std::end(result), comp); | |
return result; | |
} | |
} // namespace internal | |
// API search type: stable_sort_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence stably by given less comparator. | |
template <typename Compare, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut stable_sort_by(Compare comp, Container &&xs) { | |
return internal::stable_sort_by(internal::can_reuse_v<Container>{}, comp, | |
std::forward<Container>(xs)); | |
} | |
// API search type: stable_sort_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence stably by given transformer. | |
template <typename F, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut stable_sort_on(F f, Container &&xs) { | |
return stable_sort_by(internal::is_less_by_struct<F>(f), | |
std::forward<Container>(xs)); | |
} | |
// API search type: stable_sort : [a] -> [a] | |
// fwd bind count: 0 | |
// Sort a sequence stably to ascending order using std::less. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut stable_sort(Container &&xs) { | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return stable_sort_by(std::less<T>(), std::forward<Container>(xs)); | |
} | |
namespace internal { | |
template <typename Compare, typename Container> | |
Container partial_sort_by(internal::reuse_container_t, Compare comp, | |
std::size_t count, Container &&xs) { | |
if (count > xs.size()) { | |
count = xs.size(); | |
} | |
auto middle = std::begin(xs); | |
internal::advance_iterator(middle, count); | |
std::partial_sort(std::begin(xs), middle, std::end(xs), comp); | |
return std::forward<Container>( | |
get_segment(internal::reuse_container_t(), 0, count, xs)); | |
} | |
template <typename Compare, typename Container> | |
Container partial_sort_by(internal::create_new_container_t, Compare comp, | |
std::size_t count, const Container &xs) { | |
auto result = xs; | |
return partial_sort_by(internal::reuse_container_t(), comp, count, | |
std::move(result)); | |
} | |
} // namespace internal | |
// API search type: partial_sort_by : (((a, a) -> Bool), Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Partially sort a sequence by a given less comparator. | |
// Returns only the sorted segment. | |
template <typename Compare, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut partial_sort_by(Compare comp, std::size_t count, Container &&xs) { | |
return internal::partial_sort_by(internal::can_reuse_v<Container>{}, comp, | |
count, std::forward<Container>(xs)); | |
} | |
// API search type: partial_sort_on : ((a -> b), Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Partially sort a sequence by a given transformer. | |
// Returns only the sorted segment. | |
template <typename F, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut partial_sort_on(F f, std::size_t count, Container &&xs) { | |
return partial_sort_by(internal::is_less_by_struct<F>(f), count, | |
std::forward<Container>(xs)); | |
} | |
// API search type: partial_sort : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Partially sort a sequence in ascending order using std::less. | |
// Returns only the sorted segment. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut partial_sort(std::size_t count, Container &&xs) { | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return partial_sort_by(std::less<T>(), count, std::forward<Container>(xs)); | |
} | |
// API search type: nth_element_by : (((a, a) -> Bool), Int, [a]) -> a | |
// fwd bind count: 2 | |
// Return the nth largest element of a sequence by a given less comparator. | |
template <typename Compare, typename Container, | |
typename T = typename Container::value_type> | |
T nth_element_by(Compare comp, std::size_t n, const Container &xs) { | |
assert(n < xs.size()); | |
auto result = xs; | |
auto middle = std::begin(result); | |
internal::advance_iterator(middle, n); | |
std::nth_element(std::begin(result), middle, std::end(result), comp); | |
return *middle; | |
} | |
// API search type: nth_element_on : ((a -> b), Int, [a]) -> a | |
// fwd bind count: 2 | |
// Return the nth largest element of a sequence by a given transformer. | |
template <typename F, typename Container, | |
typename T = typename Container::value_type> | |
T nth_element_on(F f, std::size_t n, const Container &xs) { | |
return nth_element_by(internal::is_less_by_struct<F>(f), n, xs); | |
} | |
// API search type: nth_element : (Int, [a]) -> a | |
// fwd bind count: 1 | |
// Return the nth largest element of a sequence using std::less. | |
template <typename Container, typename T = typename Container::value_type> | |
T nth_element(std::size_t n, const Container &xs) { | |
return nth_element_by(std::less<T>(), n, xs); | |
} | |
namespace internal { | |
template <typename BinaryPredicate, typename Container> | |
Container unique_by(internal::reuse_container_t, BinaryPredicate pred, | |
Container &&xs) { | |
internal::check_binary_predicate_for_container<BinaryPredicate, Container>(); | |
const auto it_end = std::unique(std::begin(xs), std::end(xs), pred); | |
xs.erase(it_end, std::end(xs)); | |
return std::forward<Container>(xs); | |
} | |
template <typename BinaryPredicate, typename Container> | |
Container unique_by(internal::create_new_container_t, BinaryPredicate pred, | |
const Container &xs) { | |
auto result = xs; | |
return unique_by(internal::reuse_container_t(), pred, std::move(result)); | |
} | |
} // namespace internal | |
// API search type: unique_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Like unique, eliminates all but the first element | |
// from every consecutive group of equivalent elements from the sequence; | |
// but with a user supplied equality predicate. | |
// See nub_by for making elements globally unique in a sequence. | |
// O(n) | |
template <typename BinaryPredicate, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut unique_by(BinaryPredicate pred, Container &&xs) { | |
return internal::unique_by(internal::can_reuse_v<Container>{}, pred, | |
std::forward<Container>(xs)); | |
} | |
// API search type: unique_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Like unique, eliminates all but the first element | |
// from every consecutive group of equivalent elements from the sequence; | |
// but with a user supplied transformation (e.g. getter). | |
// See nub_on for making elements globally unique in a sequence. | |
// O(n) | |
// Also known as drop_repeats. | |
template <typename F, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut unique_on(F f, Container &&xs) { | |
return unique_by(internal::is_equal_by_struct<F>(f), | |
std::forward<Container>(xs)); | |
} | |
// API search type: unique : [a] -> [a] | |
// fwd bind count: 0 | |
// Eliminates all but the first element | |
// from every consecutive group of equivalent elements from the sequence. | |
// unique([1,2,2,3,2]) == [1,2,3,2] | |
// See nub for making elements globally unique in a sequence. | |
// O(n) | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut unique(Container &&xs) { | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return unique_on(identity<T>, std::forward<Container>(xs)); | |
} | |
// API search type: intersperse : (a, [a]) -> [a] | |
// fwd bind count: 1 | |
// Insert a value between all adjacent values in a sequence. | |
// intersperse(0, [1, 2, 3]) == [1, 0, 2, 0, 3] | |
// Also known as interpose. | |
template <typename Container, typename X = typename Container::value_type> | |
Container intersperse(const X &value, const Container &xs) { | |
if (xs.empty()) | |
return Container(); | |
if (size_of_cont(xs) == 1) | |
return xs; | |
Container result; | |
internal::prepare_container( | |
result, std::max<std::size_t>(0, size_of_cont(xs) * 2 - 1)); | |
auto it = internal::get_back_inserter(result); | |
for_each(std::begin(xs), --std::end(xs), [&value, &it](const X &x) { | |
*it = x; | |
*it = value; | |
}); | |
*it = xs.back(); | |
return result; | |
} | |
// API search type: join : ([a], [[a]]) -> [a] | |
// fwd bind count: 1 | |
// Inserts a separator sequence in between the elements | |
// of a sequence of sequences and concatenates the result. | |
// Also known as intercalate or implode. | |
// join(", ", ["a", "bee", "cee"]) == "a, bee, cee" | |
// join([0, 0], [[1], [2], [3, 4]]) == [1, 0, 0, 2, 0, 0, 3, 4] | |
template <typename Container, typename X = typename Container::value_type> | |
X join(const X &separator, const Container &xs) { | |
return concat(intersperse(separator, xs)); | |
} | |
// API search type: join_elem : (a, [[a]]) -> [a] | |
// fwd bind count: 1 | |
// Inserts a separator in between the elements | |
// of a sequence of sequences and concatenates the result. | |
// Also known as intercalate_elem. | |
// join_elem(';', ["a", "bee", "cee"]) == "a;bee;cee" | |
// join_elem(0, [[1], [2], [3, 4]]) == [1, 0, 2, 0, 3, 4]] | |
template <typename Container, typename Inner = typename Container::value_type, | |
typename X = typename Inner::value_type> | |
Inner join_elem(const X &separator, const Container &xs) { | |
return join(Inner(1, separator), xs); | |
} | |
// API search type: is_elem_of_by : ((a -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if at least one element of the container fulfils a predicate. | |
// is_elem_of_by((==), [1,2,3]) == true | |
template <typename UnaryPredicate, typename Container> | |
bool is_elem_of_by(UnaryPredicate pred, const Container &xs) { | |
return std::find_if(std::begin(xs), std::end(xs), pred) != std::end(xs); | |
} | |
// API search type: is_elem_of : (a, [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if an element is a member of a container. | |
// is_elem_of(2, [1,2,3]) == true | |
// Also known as flip(contains). | |
template <typename Container> | |
bool is_elem_of(const typename Container::value_type &x, const Container &xs) { | |
return is_elem_of_by(is_equal_to(x), xs); | |
} | |
// API search type: nub_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Makes the elements in a container unique with respect to a predicate | |
// nub_by((==), [1,2,2,3,2]) == [1,2,3] | |
// O(n^2) | |
template <typename Container, typename BinaryPredicate> | |
Container nub_by(BinaryPredicate p, const Container &xs) { | |
Container result; | |
auto itOut = internal::get_back_inserter(result); | |
for (const auto &x : xs) { | |
auto eqToX = bind_1st_of_2(p, x); | |
if (!is_elem_of_by(eqToX, result)) { | |
*itOut = x; | |
} | |
} | |
return result; | |
} | |
// API search type: nub_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Makes the elements in a container unique | |
// with respect to their function value. | |
// nub_on((mod 10), [12,32,15]) == [12,15] | |
// O(n^2) | |
template <typename Container, typename F> | |
Container nub_on(F f, const Container &xs) { | |
return nub_by(is_equal_by(f), xs); | |
} | |
// API search type: nub : [a] -> [a] | |
// fwd bind count: 0 | |
// Makes the elements in a container unique. | |
// nub([1,2,2,3,2]) == [1,2,3] | |
// O(n^2) | |
// Also known as distinct. | |
template <typename Container> Container nub(const Container &xs) { | |
typedef typename Container::value_type T; | |
return nub_by(std::equal_to<T>(), xs); | |
} | |
// API search type: all_unique_by_eq : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all elements in a container are unique | |
// with respect to a predicate. | |
// Returns true for empty containers. | |
// O(n^2) | |
template <typename Container, typename BinaryPredicate> | |
bool all_unique_by_eq(BinaryPredicate p, const Container &xs) { | |
internal::check_binary_predicate_for_container<BinaryPredicate, Container>(); | |
return size_of_cont(nub_by(p, xs)) == size_of_cont(xs); | |
} | |
// API search type: all_unique_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all elements in a container are unique | |
// with respect to their function values. | |
// Returns true for empty containers. | |
// O(n^2) | |
template <typename Container, typename F> | |
bool all_unique_on(F f, const Container &xs) { | |
return all_unique_by_eq(is_equal_by(f), xs); | |
} | |
// API search type: all_unique : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if all elements in a container are unique. | |
// Returns true for empty containers. | |
// O(n^2) | |
template <typename Container> bool all_unique(const Container &xs) { | |
typedef typename Container::value_type T; | |
auto comp = std::equal_to<T>(); | |
return all_unique_by_eq(comp, xs); | |
} | |
// API search type: is_strictly_sorted_by : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is strictly sorted using a predicate. | |
// comp(a, b) must return true only if a < b. | |
// O(n) | |
template <typename Container, typename Compare> | |
bool is_strictly_sorted_by(Compare comp, const Container &xs) { | |
internal::check_compare_for_container<Compare, Container>(); | |
if (size_of_cont(xs) < 2) | |
return true; | |
auto it1 = std::begin(xs); | |
for (auto it2 = it1 + 1; it2 < std::end(xs); ++it1, ++it2) | |
if (!internal::invoke(comp, *it1, *it2)) | |
return false; | |
return true; | |
} | |
// API search type: is_strictly_sorted_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is strictly sorted using a transformer. | |
// O(n) | |
template <typename Container, typename F> | |
bool is_strictly_sorted_on(F f, const Container &xs) { | |
return is_strictly_sorted_by(is_less_by(f), xs); | |
} | |
// API search type: is_strictly_sorted : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if a container already is strictly sorted | |
// in ascending order using std::less. | |
// O(n) | |
template <typename Container> bool is_strictly_sorted(const Container &xs) { | |
typedef typename Container::value_type T; | |
auto comp = std::less<T>(); | |
return is_strictly_sorted_by(comp, xs); | |
} | |
// API search type: is_sorted_by : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is sorted using a predicate. | |
// comp(a, b) must return true only if a < b. | |
// O(n) | |
template <typename Container, typename Compare> | |
bool is_sorted_by(Compare comp, const Container &xs) { | |
internal::check_compare_for_container<Compare, Container>(); | |
if (size_of_cont(xs) < 2) | |
return true; | |
auto it1 = std::begin(xs); | |
for (auto it2 = it1 + 1; it2 < std::end(xs); ++it1, ++it2) | |
if (internal::invoke(comp, *it2, *it1)) | |
return false; | |
return true; | |
} | |
// API search type: is_sorted_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is strictly sorted using a transformer. | |
// O(n) | |
template <typename Container, typename F> | |
bool is_sorted_on(F f, const Container &xs) { | |
return is_sorted_by(is_less_by(f), xs); | |
} | |
// API search type: is_sorted : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if a container already is sorted | |
// in ascending order using std::less. | |
// O(n) | |
template <typename Container> bool is_sorted(const Container &xs) { | |
typedef typename Container::value_type T; | |
auto comp = std::less<T>(); | |
return is_sorted_by(comp, xs); | |
} | |
// API search type: is_prefix_of : ([a], [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a containers starts with a token. | |
// is_prefix_of("Fun", "FunctionalPlus") == true | |
template <typename Container> | |
bool is_prefix_of(const Container &token, const Container &xs) { | |
if (size_of_cont(token) > size_of_cont(xs)) | |
return false; | |
return get_segment(0, size_of_cont(token), xs) == token; | |
} | |
// API search type: is_suffix_of : ([a], [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a containers contains a token as a segment. | |
// is_suffix_of("us", "FunctionalPlus") == true | |
template <typename Container> | |
bool is_suffix_of(const Container &token, const Container &xs) { | |
if (size_of_cont(token) > size_of_cont(xs)) | |
return false; | |
return get_segment(size_of_cont(xs) - size_of_cont(token), size_of_cont(xs), | |
xs) == token; | |
} | |
// API search type: all_by : ((a -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a containers contains a token as a segment. | |
// all_by(is_even, [2, 4, 6]) == true | |
// Returns true for empty containers. | |
template <typename UnaryPredicate, typename Container> | |
bool all_by(UnaryPredicate p, const Container &xs) { | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
return std::all_of(std::begin(xs), std::end(xs), p); | |
} | |
// API search type: all : [Bool] -> Bool | |
// fwd bind count: 0 | |
// Checks if all values in a container evaluate to true. | |
// all([true, false, true]) == false | |
// Returns true for empty containers. | |
template <typename Container> bool all(const Container &xs) { | |
typedef typename Container::value_type T; | |
return all_by(identity<T>, xs); | |
} | |
// API search type: all_the_same_by : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all values in a container are equal using a binary predicate. | |
// Returns true for empty containers. | |
template <typename Container, typename BinaryPredicate> | |
bool all_the_same_by(BinaryPredicate p, const Container &xs) { | |
internal::check_binary_predicate_for_container<BinaryPredicate, Container>(); | |
if (size_of_cont(xs) < 2) | |
return true; | |
auto unaryPredicate = bind_1st_of_2(p, xs.front()); | |
return all_by(unaryPredicate, xs); | |
} | |
// API search type: all_the_same_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all values in a container are equal using a transformer. | |
// Returns true for empty containers. | |
template <typename Container, typename F> | |
bool all_the_same_on(F f, const Container &xs) { | |
if (size_of_cont(xs) < 2) | |
return true; | |
auto unaryPredicate = is_equal_by_to(f, f(xs.front())); | |
return all_by(unaryPredicate, xs); | |
} | |
// API search type: all_the_same : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if all values in a container are equal. | |
// Returns true for empty containers. | |
template <typename Container> bool all_the_same(const Container &xs) { | |
typedef typename Container::value_type T; | |
auto binaryPredicate = std::equal_to<T>(); | |
return all_the_same_by(binaryPredicate, xs); | |
} | |
// API search type: numbers_step : (a, a, a) -> [a] | |
// fwd bind count: 2 | |
// Return a sequence of numbers using a specific step. | |
// numbers_step(2, 9, 2) == [2, 4, 6, 8] | |
template <typename T, typename ContainerOut = std::vector<T>> | |
ContainerOut numbers_step(const T start, const T end, const T step) { | |
ContainerOut result; | |
if ((step > 0 && start >= end) || (step < 0 && start <= end) || step == 0) { | |
return result; | |
} | |
std::size_t size = static_cast<std::size_t>((end - start) / step); | |
internal::prepare_container(result, size); | |
auto it = internal::get_back_inserter<ContainerOut>(result); | |
for (T x = start; x < end; x += step) | |
*it = x; | |
return result; | |
} | |
// API search type: numbers : (a, a) -> [a] | |
// fwd bind count: 1 | |
// Return an ascending sequence of numbers.. | |
// Also known as range. | |
// numbers(2, 9) == [2, 3, 4, 5, 6, 7, 8] | |
template <typename T, typename ContainerOut = std::vector<T>> | |
ContainerOut numbers(const T start, const T end) { | |
return numbers_step<T, ContainerOut>(start, end, 1); | |
} | |
// API search type: singleton_seq : a -> [a] | |
// fwd bind count: 0 | |
// Construct a sequence containing a single value. | |
// singleton_seq(3) == [3]. | |
template <typename T, typename ContainerOut = std::vector<T>> | |
ContainerOut singleton_seq(const T &x) { | |
return ContainerOut(1, x); | |
} | |
// API search type: all_idxs : [a] -> [Int] | |
// fwd bind count: 0 | |
// Returns a vector containing all valid indices of sequence xs. | |
// all_idxs([6,4,7,6]) == [0,1,2,3] | |
template <typename Container> | |
std::vector<std::size_t> all_idxs(const Container &xs) { | |
return numbers<std::size_t>(0, size_of_cont(xs)); | |
} | |
// API search type: init : [a] -> [a] | |
// fwd bind count: 0 | |
// init([0,1,2,3]) == [0,1,2] | |
// Unsafe! xs must be non-empty. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut init(Container &&xs) { | |
assert(!is_empty(xs)); | |
return get_segment(0, size_of_cont(std::forward<Container>(xs)) - 1, xs); | |
} | |
// API search type: tail : [a] -> [a] | |
// fwd bind count: 0 | |
// Drops the first element of a container, keeps the rest. Unsafe! | |
// tail([0,1,2,3]) == [1,2,3] | |
// Unsafe! xs must be non-empty. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut tail(Container &&xs) { | |
assert(!is_empty(xs)); | |
return get_segment(1, size_of_cont(std::forward<Container>(xs)), xs); | |
} | |
// API search type: head : [a] -> a | |
// fwd bind count: 0 | |
// Return the first element of a container. | |
// head([0,1,2,3]) == 0 | |
// Unsafe! xs must be non-empty. | |
template <typename Container> | |
typename Container::value_type head(const Container &xs) { | |
assert(!is_empty(xs)); | |
return xs.front(); | |
} | |
// API search type: last : [a] -> a | |
// fwd bind count: 0 | |
// Return the last element of a container. | |
// last([0,1,2,3]) == 3 | |
// Unsafe! xs must be non-empty. | |
template <typename Container> | |
typename Container::value_type last(const Container &xs) { | |
assert(!is_empty(xs)); | |
return xs.back(); | |
} | |
// API search type: mean_stddev : [a] -> (a, a) | |
// fwd bind count: 0 | |
// Calculates the mean and the population standard deviation. | |
// mean_stddev([4, 8]) == (6, 2) | |
// mean_stddev([1, 3, 7, 4]) == (3.75, 2.5) | |
// xs must be non-empty. | |
template <typename Result, typename Container> | |
std::pair<Result, Result> mean_stddev(const Container &xs) { | |
assert(size_of_cont(xs) != 0); | |
// http://stackoverflow.com/a/7616783/1866775 | |
Result sum = static_cast<Result>(internal::accumulate( | |
xs.begin(), xs.end(), static_cast<typename Container::value_type>(0))); | |
Result mean = sum / static_cast<Result>(xs.size()); | |
std::vector<Result> diff(xs.size()); | |
std::transform(xs.begin(), xs.end(), diff.begin(), | |
[mean](Result x) { return x - mean; }); | |
Result sq_sum = std::inner_product(diff.begin(), diff.end(), diff.begin(), | |
static_cast<Result>(0)); | |
Result stddev = std::sqrt(sq_sum / static_cast<Result>(xs.size())); | |
return std::make_pair(mean, stddev); | |
} | |
// API search type: count_occurrences_by : ((a -> b), [a]) -> Map b Int | |
// fwd bind count: 1 | |
// Returns a discrete frequency distribution of the elements in a container | |
// applying a specific transformer. | |
// count_occurrences_by(floor, [1.1, 2.3, 2.7, 3.6, 2.4]) == [(1, 1), (2, 3), | |
// (3, 1)] O(n) | |
template <typename F, typename ContainerIn> | |
auto count_occurrences_by(F f, const ContainerIn &xs) { | |
using In = typename ContainerIn::value_type; | |
using MapOut = | |
std::map<std::decay_t<internal::invoke_result_t<F, In>>, std::size_t>; | |
internal::trigger_static_asserts<internal::unary_function_tag, F, | |
typename ContainerIn::value_type>(); | |
MapOut result; | |
for (const auto &x : xs) { | |
++result[internal::invoke(f, x)]; | |
} | |
return result; | |
} | |
// API search type: count_occurrences : [a] -> Map a Int | |
// fwd bind count: 0 | |
// Returns a discrete frequency distribution of the elements in a container | |
// applying a specific transformer. | |
// Can be used to create a histogram. | |
// count_occurrences([1,2,2,3,2]) == [(1, 1), (2, 3), (3, 1)] | |
// O(n) | |
template <typename ContainerIn, | |
typename MapOut = | |
typename std::map<typename ContainerIn::value_type, std::size_t>> | |
MapOut count_occurrences(const ContainerIn &xs) { | |
return count_occurrences_by(identity<typename ContainerIn::value_type>, xs); | |
} | |
// API search type: lexicographical_less_by : (((a, a) -> Bool), [a], [a]) -> | |
// Bool fwd bind count: 2 Lexicographical less-than comparison using a specific | |
// predicate. lexicographical_less_by((<), [0,1,2,2,4,5], [0,1,2,3,4,5]) == true | |
// lexicographical_less_by((<), "012245", "012345") == true | |
// lexicographical_less_by((<), "01234", "012345") == true | |
// lexicographical_less_by((<), "012345", "01234") == false | |
// lexicographical_less_by((<), "012345", "012345") == false | |
template <typename Container, typename BinaryPredicate> | |
bool lexicographical_less_by(BinaryPredicate p, const Container &xs, | |
const Container &ys) { | |
internal::check_binary_predicate_for_container<BinaryPredicate, Container>(); | |
auto itXs = std::begin(xs); | |
auto itYs = std::begin(ys); | |
while (itXs != std::end(xs) && itYs != std::end(ys)) { | |
if (internal::invoke(p, *itXs, *itYs)) { | |
return true; | |
} | |
if (internal::invoke(p, *itYs, *itXs)) { | |
return false; | |
} | |
++itXs; | |
++itYs; | |
} | |
if (size_of_cont(xs) < size_of_cont(ys)) { | |
return true; | |
} | |
return false; | |
} | |
// API search type: lexicographical_less : ([a], [a]) -> Bool | |
// fwd bind count: 1 | |
// Lexicographical less-than comparison. | |
// lexicographical_less([0,1,2,2,4,5], [0,1,2,3,4,5]) == true | |
// lexicographical_less("012245", "012345") == true | |
// lexicographical_less("01234", "012345") == true | |
// lexicographical_less("012345", "01234") == false | |
// lexicographical_less("012345", "012345") == false | |
template <typename Container> | |
bool lexicographical_less(const Container &xs, const Container &ys) { | |
return lexicographical_less_by(is_less<typename Container::value_type>, xs, | |
ys); | |
} | |
// API search type: lexicographical_sort : [[a]] -> [[a]] | |
// fwd bind count: 0 | |
// sort by lexicographical_less | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut lexicographical_sort(Container &&xs) { | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return sort_by(lexicographical_less<T>, std::forward<Container>(xs)); | |
} | |
// API search type: replicate : (Int, a) -> [a] | |
// fwd bind count: 1 | |
// Create a sequence containing x n times. | |
// replicate(3, 1) == [1, 1, 1] | |
template <typename T, typename ContainerOut = std::vector<T>> | |
ContainerOut replicate(std::size_t n, const T &x) { | |
return ContainerOut(n, x); | |
} | |
namespace internal { | |
template <typename UnaryPredicate, typename T> | |
T instead_of_if(internal::reuse_container_t, UnaryPredicate pred, const T &alt, | |
T &&x) { | |
if (internal::invoke(pred, x)) | |
return alt; | |
else | |
return std::forward<T>(x); | |
} | |
template <typename UnaryPredicate, typename T> | |
T instead_of_if(internal::create_new_container_t, UnaryPredicate pred, | |
const T &alt, const T &x) { | |
if (internal::invoke(pred, x)) | |
return alt; | |
else | |
return x; | |
} | |
} // namespace internal | |
// API search type: instead_of_if : ((a -> Bool), a, a) -> a | |
// fwd bind count: 2 | |
// Return alt if pred(x), otherwise x itself. | |
template <typename UnaryPredicate, typename T, typename TAlt> | |
auto instead_of_if(UnaryPredicate pred, const TAlt &alt, T &&x) { | |
return internal::instead_of_if(internal::can_reuse_v<T>{}, pred, alt, | |
std::forward<T>(x)); | |
} | |
// API search type: instead_of_if_empty : ((a -> Bool), [a], [a]) -> [a] | |
// fwd bind count: 2 | |
// Return alt if xs is empty, otherwise xs itself. | |
template <typename Container, typename ContainerAlt, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut instead_of_if_empty(const ContainerAlt &alt, Container &&xs) { | |
return instead_of_if(is_empty<internal::remove_const_and_ref_t<Container>>, | |
alt, std::forward<Container>(xs)); | |
} | |
} // namespace fplus |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. | |
// https://github.com/Dobiasd/FunctionalPlus | |
// Distributed under the Boost Software License, Version 1.0. | |
// (See accompanying file LICENSE_1_0.txt or copy at | |
// http://www.boost.org/LICENSE_1_0.txt) | |
#pragma once | |
#include <fplus/compare.hpp> | |
#include <fplus/composition.hpp> | |
#include <fplus/container_traits.hpp> | |
#include <fplus/maybe.hpp> | |
#include <fplus/internal/container_common.hpp> | |
#include <fplus/internal/invoke.hpp> | |
#include <fplus/internal/meta.hpp> | |
#include <algorithm> | |
#include <cassert> | |
#include <cmath> | |
#include <cstddef> | |
#include <iterator> | |
#include <numeric> | |
namespace fplus | |
{ | |
namespace internal | |
{ | |
template <typename UnaryPredicate, typename Container> void check_unary_predicate_for_container() | |
{ | |
internal::check_unary_predicate_for_type<UnaryPredicate, typename Container::value_type>(); | |
} | |
template <typename F, typename Container> void check_index_with_type_predicate_for_container() | |
{ | |
typedef typename Container::value_type T; | |
internal::trigger_static_asserts<internal::binary_function_tag, F, std::size_t, T>(); | |
static_assert(std::is_convertible<internal::invoke_result_t<F, std::size_t, T>, bool>::value, | |
"Function must return bool."); | |
} | |
template <typename Compare, typename Container> void check_compare_for_container() | |
{ | |
typedef typename Container::value_type T; | |
internal::trigger_static_asserts<internal::binary_predicate_tag, Compare, T, T>(); | |
} | |
template <typename BinaryPredicate, typename Container> void check_binary_predicate_for_container() | |
{ | |
typedef typename Container::value_type T; | |
internal::trigger_static_asserts<internal::binary_predicate_tag, BinaryPredicate, T, T>(); | |
} | |
// PrepareContainer and BackInserter are overloaded | |
// to increase performance on std::vector and std::string | |
// by using std::vector<T>::reserve | |
// and std::back_inserter instead of std::back_inserter. | |
// In VC2015, release mode, Celsius W520 Xeon | |
// this leads to an increase in performance of about a factor of 3 | |
// for transform. | |
template <typename C> | |
void prepare_container(const std::basic_string<C, std::char_traits<C>, std::allocator<C>> &ys, std::size_t size) | |
{ | |
ys.reserve(size); | |
} | |
template <typename Y> void prepare_container(std::vector<Y> &ys, std::size_t size) | |
{ | |
ys.reserve(size); | |
} | |
template <typename T, std::size_t N> void prepare_container(std::array<T, N> &, std::size_t size) | |
{ | |
assert(size == N); | |
unused(size); | |
} | |
template <typename Y> void prepare_container(std::unordered_set<Y> &ys, std::size_t size) | |
{ | |
ys.reserve(size); | |
} | |
template <typename Key, typename T> void prepare_container(std::unordered_map<Key, T> &ys, std::size_t size) | |
{ | |
ys.reserve(size); | |
} | |
template <typename Y> void prepare_container(std::unordered_multiset<Y> &ys, std::size_t size) | |
{ | |
ys.reserve(size); | |
} | |
template <typename Key, typename T> void prepare_container(std::unordered_multimap<Key, T> &ys, std::size_t size) | |
{ | |
ys.reserve(size); | |
} | |
template <typename Container> void prepare_container(Container &, std::size_t) | |
{ | |
} | |
template <typename Container> std::back_insert_iterator<Container> get_back_inserter(std::string &ys) | |
{ | |
return std::back_inserter(ys); | |
} | |
template <typename Container, typename Y> std::back_insert_iterator<Container> get_back_inserter(std::vector<Y> &ys) | |
{ | |
return std::back_inserter(ys); | |
} | |
template <typename Container, typename Y> std::back_insert_iterator<Container> get_back_inserter(std::list<Y> &ys) | |
{ | |
return std::back_inserter(ys); | |
} | |
template <typename Container, typename Y> std::back_insert_iterator<Container> get_back_inserter(std::deque<Y> &ys) | |
{ | |
return std::back_inserter(ys); | |
} | |
// Avoid self-assignment. | |
template <typename T> void assign(T &x, T &&y) | |
{ | |
if (&x != &y) | |
x = std::move(y); | |
} | |
template <typename T, std::size_t N> | |
struct array_back_insert_iterator : public std::back_insert_iterator<std::array<T, N>> | |
{ | |
typedef std::back_insert_iterator<std::array<T, N>> base_type; | |
explicit array_back_insert_iterator(std::array<T, N> &arr) : base_type(arr), arr_ptr_(&arr), pos_(0) | |
{ | |
} | |
array_back_insert_iterator(const array_back_insert_iterator<T, N> &other) | |
: base_type(*other.arr_ptr_), arr_ptr_(other.arr_ptr_), pos_(other.pos_) | |
{ | |
} | |
array_back_insert_iterator<T, N> &operator=(const array_back_insert_iterator<T, N> &other) | |
{ | |
arr_ptr_ = other.arr_ptr_; | |
pos_ = other.pos_; | |
return *this; | |
} | |
~array_back_insert_iterator() | |
{ | |
assert(pos_ == 0 || pos_ == N); | |
} | |
array_back_insert_iterator<T, N> &operator=(const T &x) | |
{ | |
assert(pos_ < N); | |
(*arr_ptr_)[pos_] = x; | |
++pos_; | |
return *this; | |
} | |
array_back_insert_iterator<T, N> &operator=(T &&x) | |
{ | |
assert(pos_ < N); | |
assign((*arr_ptr_)[pos_], std::move(x)); | |
++pos_; | |
return *this; | |
} | |
array_back_insert_iterator<T, N> &operator*() | |
{ | |
return *this; | |
} | |
array_back_insert_iterator<T, N> &operator++() | |
{ | |
return *this; | |
} | |
array_back_insert_iterator<T, N> operator++(int) | |
{ | |
return *this; | |
} | |
private: | |
std::array<T, N> *arr_ptr_; | |
std::size_t pos_; | |
}; | |
#if defined(_MSC_VER) && _MSC_VER >= 1900 | |
template <typename T, std::size_t N> struct std::_Is_checked_helper<array_back_insert_iterator<T, N>> : public true_type | |
{ // mark array_back_insert_iterator as checked | |
}; | |
#endif | |
template <typename Container, typename Y, std::size_t N> | |
array_back_insert_iterator<Y, N> get_back_inserter(std::array<Y, N> &ys) | |
{ | |
return array_back_insert_iterator<Y, N>(ys); | |
} | |
template <typename Container> std::insert_iterator<Container> get_back_inserter(Container &ys) | |
{ | |
return std::inserter(ys, std::end(ys)); | |
} | |
template <typename Iterator> void advance_iterator(Iterator &it, std::size_t distance) | |
{ | |
std::advance(it, static_cast<typename Iterator::difference_type>(distance)); | |
} | |
template <typename T> void advance_iterator(T *&it, std::size_t distance) | |
{ | |
it += static_cast<std::ptrdiff_t>(distance); | |
} | |
template <typename Iterator> Iterator add_to_iterator(Iterator it, std::size_t distance = 1) | |
{ | |
return std::next(it, static_cast<typename Iterator::difference_type>(distance)); | |
} | |
// GCC 4.9 does not support std::rbegin, std::rend and std::make_reverse_iterator | |
template <typename Iterator> std::reverse_iterator<Iterator> make_reverse_iterator(Iterator it) | |
{ | |
return std::reverse_iterator<Iterator>(it); | |
} | |
} // namespace internal | |
// API search type: is_even : Int -> Bool | |
// fwd bind count: 0 | |
// Checks if x is even. | |
template <typename X> bool is_even(X x) | |
{ | |
static_assert(std::is_integral<X>::value, "type must be integral"); | |
return x % 2 == 0; | |
} | |
// API search type: is_odd : Int -> Bool | |
// fwd bind count: 0 | |
// Checks if x is odd. | |
template <typename X> bool is_odd(X x) | |
{ | |
static_assert(std::is_integral<X>::value, "type must be integral"); | |
return x % 2 != 0; | |
} | |
// API search type: is_empty : [a] -> Bool | |
// fwd bind count: 0 | |
// Returns true if the container holds no elements. | |
// is_empty([1, 2]) == false | |
// is_empty([]) == true | |
template <typename Container> bool is_empty(const Container &xs) | |
{ | |
return xs.empty(); | |
} | |
// API search type: is_not_empty : [a] -> Bool | |
// fwd bind count: 0 | |
// Returns true if the container holds at least one element. | |
// is_not_empty([1, 2]) == true | |
template <typename Container> bool is_not_empty(const Container &xs) | |
{ | |
return !is_empty(xs); | |
} | |
// API search type: size_of_cont : [a] -> Int | |
// fwd bind count: 0 | |
// Returns the number of elements in the given container. | |
// size_of_cont([3, 4]) == 2 | |
template <typename Container> std::size_t size_of_cont(const Container &xs) | |
{ | |
return xs.size(); | |
} | |
// API search type: convert : a -> b | |
// fwd bind count: 0 | |
// Converts one type of element into another. | |
template <typename Dest, typename Source> Dest convert(const Source &x) | |
{ | |
return Dest(x); | |
} | |
// API search type: convert_elems : [a] -> [b] | |
// fwd bind count: 0 | |
// Converts all elements in a sequence to a different type. | |
// convert_elems<NewT>([1, 2, 3]) == [NewT(1), NewT(2), NewT(3)] | |
template <typename NewT, typename ContainerIn, | |
typename ContainerOut = typename internal::same_cont_new_t<ContainerIn, NewT, 0>::type> | |
ContainerOut convert_elems(const ContainerIn &xs) | |
{ | |
static_assert(std::is_constructible<NewT, typename ContainerIn::value_type>::value, "Elements not convertible."); | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it = internal::get_back_inserter<ContainerOut>(ys); | |
// using 'for (const auto& x ...)' is even for ints as fast as | |
// using 'for (int x ...)' (GCC, O3), so there is no need to | |
// check if the type is fundamental and then dispatch accordingly. | |
for (const auto &x : xs) | |
{ | |
*it = convert<NewT>(x); | |
} | |
return ys; | |
} | |
// API search type: convert_container : [a] -> [a] | |
// fwd bind count: 0 | |
// Change the type of the container | |
// while keeping the elements in the sequence the same. | |
// convert_container([1, 2, 3]) == [1, 2, 3] | |
// Useful for example if you want to convert an std::list to an std::vector. | |
template <typename ContainerOut, typename ContainerIn> ContainerOut convert_container(const ContainerIn &xs) | |
{ | |
typedef typename ContainerIn::value_type SourceElem; | |
typedef typename ContainerOut::value_type DestElem; | |
static_assert(std::is_same<DestElem, SourceElem>::value, "Source and dest container must have the same value_type"); | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto itOut = internal::get_back_inserter<ContainerOut>(ys); | |
std::copy(std::begin(xs), std::end(xs), itOut); | |
return ys; | |
} | |
// API search type: convert_container_and_elems : [a] -> [b] | |
// fwd bind count: 0 | |
// Converts between different containers and elements. | |
// Dest elements are allowed to have explicit constructors. | |
// convert([1, 2, 3]) == [1, 2, 3] | |
template <typename ContainerOut, typename ContainerIn> ContainerOut convert_container_and_elems(const ContainerIn &xs) | |
{ | |
static_assert(std::is_convertible<typename ContainerIn::value_type, typename ContainerOut::value_type>::value, | |
"Elements not convertible."); | |
typedef typename ContainerOut::value_type DestElem; | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it = internal::get_back_inserter<ContainerOut>(ys); | |
for (const auto &x : xs) | |
{ | |
*it = convert<DestElem>(x); | |
} | |
return ys; | |
} | |
namespace internal | |
{ | |
template <typename Container> | |
Container get_segment(internal::reuse_container_t, std::size_t idx_begin, std::size_t idx_end, Container &&xs) | |
{ | |
idx_end = std::min(idx_end, size_of_cont(xs)); | |
if (idx_end <= idx_begin) | |
{ | |
xs.clear(); | |
return std::forward<Container>(xs); | |
} | |
auto itBegin = std::begin(xs); | |
internal::advance_iterator(itBegin, idx_begin); | |
auto itEnd = itBegin; | |
internal::advance_iterator(itEnd, idx_end - idx_begin); | |
xs.erase(std::copy(itBegin, itEnd, std::begin(xs)), std::end(xs)); | |
return std::forward<Container>(xs); | |
} | |
template <typename Container> | |
Container get_segment(internal::create_new_container_t, std::size_t idx_begin, std::size_t idx_end, const Container &xs) | |
{ | |
idx_end = std::min(idx_end, size_of_cont(xs)); | |
if (idx_end <= idx_begin) | |
{ | |
return {}; | |
} | |
Container result; | |
auto itBegin = std::begin(xs); | |
internal::advance_iterator(itBegin, idx_begin); | |
auto itEnd = itBegin; | |
internal::advance_iterator(itEnd, idx_end - idx_begin); | |
std::copy(itBegin, itEnd, internal::get_back_inserter(result)); | |
return result; | |
} | |
} // namespace internal | |
// API search type: get_segment : (Int, Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Return a defined segment from the sequence. | |
// get_segment(2, 5, [0,1,2,3,4,5,6,7,8]) == [2,3,4] | |
// get_segment(2, 15, [0,1,2,3,4,5,6,7,8]) == [2,3,4,5,6,7,8] | |
// get_segment(5, 2, [0,1,2,3,4,5,6,7,8]) == [] | |
// Also known as slice. | |
template <typename Container, typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut get_segment(std::size_t idx_begin, std::size_t idx_end, Container &&xs) | |
{ | |
return internal::get_segment(internal::can_reuse_v<Container>{}, idx_begin, idx_end, std::forward<Container>(xs)); | |
} | |
namespace internal | |
{ | |
template <typename ContainerToken, typename Container> | |
Container set_segment(internal::reuse_container_t, std::size_t idx_begin, const ContainerToken &token, Container &&xs) | |
{ | |
assert(idx_begin + size_of_cont(token) < size_of_cont(xs)); | |
auto itBegin = std::begin(xs); | |
internal::advance_iterator(itBegin, idx_begin); | |
std::copy(std::begin(token), std::end(token), itBegin); | |
return std::forward<Container>(xs); | |
} | |
template <typename ContainerToken, typename Container> | |
Container set_segment(internal::create_new_container_t, std::size_t idx_begin, const ContainerToken &token, | |
const Container &xs) | |
{ | |
Container result = xs; | |
return set_segment(internal::reuse_container_t(), idx_begin, token, std::move(result)); | |
} | |
} // namespace internal | |
// API search type: set_segment : (Int, [a], [a]) -> [a] | |
// fwd bind count: 2 | |
// Replace part of a sequence with a token. | |
// set_segment(2, [9,9,9], [0,1,2,3,4,5,6,7,8]) == [0,1,9,9,9,5,6,7,8] | |
// Crashes on invalid indices. | |
// Also known as replace_segment. | |
template <typename ContainerToken, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut set_segment(std::size_t idx_begin, const ContainerToken &token, Container &&xs) | |
{ | |
return internal::set_segment(internal::can_reuse_v<Container>{}, idx_begin, token, std::forward<Container>(xs)); | |
} | |
namespace internal | |
{ | |
template <typename Container> | |
Container remove_segment(internal::reuse_container_t, std::size_t idx_begin, std::size_t idx_end, Container &&xs) | |
{ | |
assert(idx_begin <= idx_end); | |
assert(idx_end <= size_of_cont(xs)); | |
auto firstBreakIt = std::begin(xs); | |
internal::advance_iterator(firstBreakIt, idx_begin); | |
auto secondBreakIt = std::begin(xs); | |
internal::advance_iterator(secondBreakIt, idx_end); | |
xs.erase(std::copy(secondBreakIt, std::end(xs), firstBreakIt), std::end(xs)); | |
return std::forward<Container>(xs); | |
} | |
template <typename Container> | |
Container remove_segment(internal::create_new_container_t, std::size_t idx_begin, std::size_t idx_end, | |
const Container &xs) | |
{ | |
assert(idx_begin <= idx_end); | |
assert(idx_end <= size_of_cont(xs)); | |
Container result; | |
std::size_t length = idx_end - idx_begin; | |
internal::prepare_container(result, size_of_cont(xs) - length); | |
auto firstBreakIt = std::begin(xs); | |
internal::advance_iterator(firstBreakIt, idx_begin); | |
std::copy(std::begin(xs), firstBreakIt, internal::get_back_inserter(result)); | |
auto secondBreakIt = std::begin(xs); | |
internal::advance_iterator(secondBreakIt, idx_end); | |
std::copy(secondBreakIt, std::end(xs), internal::get_back_inserter(result)); | |
return result; | |
} | |
} // namespace internal | |
// API search type: remove_segment : (Int, Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Cuts our a defined segment from the sequence. | |
// remove_segment(2, 5, [0,1,2,3,4,5,6,7]) == [0,1,5,6,7] | |
// crashes on invalid indices | |
template <typename Container, typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut remove_segment(std::size_t idx_begin, std::size_t idx_end, Container &&xs) | |
{ | |
return internal::remove_segment(internal::can_reuse_v<Container>{}, idx_begin, idx_end, | |
std::forward<Container>(xs)); | |
} | |
// API search type: insert_at : (Int, [a], [a]) -> [a] | |
// fwd bind count: 2 | |
// Inserts a token into a sequence at a specific position. | |
// insert_at(2, [8,9], [0,1,2,3,4]) == [0,1,8,9,2,3,4] | |
// Unsafe! Crashes on invalid index. | |
template <typename Container> Container insert_at(std::size_t idx_begin, const Container &token, const Container &xs) | |
{ | |
assert(idx_begin <= size_of_cont(xs)); | |
Container result; | |
internal::prepare_container(result, size_of_cont(xs) + size_of_cont(token)); | |
auto breakIt = std::begin(xs); | |
internal::advance_iterator(breakIt, idx_begin); | |
std::copy(std::begin(xs), breakIt, internal::get_back_inserter(result)); | |
std::copy(std::begin(token), std::end(token), internal::get_back_inserter(result)); | |
std::copy(breakIt, std::end(xs), internal::get_back_inserter(result)); | |
return result; | |
} | |
// API search type: elem_at_idx : (Int, [a]) -> a | |
// fwd bind count: 1 | |
// Return the nth element of a sequence. | |
// elem_at_idx(2, [7,6,5,4,3]) == 5 | |
// Unsafe! Crashes on invalid index. | |
template <typename Container> auto elem_at_idx(std::size_t idx, const Container &xs) | |
{ | |
assert(idx < size_of_cont(xs)); | |
auto it = std::begin(xs); | |
internal::advance_iterator(it, idx); | |
return *it; | |
} | |
// API search type: elem_at_idx_maybe : (Int, [a]) -> Maybe a | |
// fwd bind count: 1 | |
// Return the nth element of a sequence if existing. | |
// elem_at_idx_maybe(2, [7,6,5,4,3]) == Just 5 | |
// elem_at_idx_maybe(9, [7,6,5,4,3]) == Nothing | |
// Use elem_at_idx_or_nothing if you want to provide a signed index type. | |
template <typename Container, typename T = typename Container::value_type> | |
maybe<T> elem_at_idx_maybe(std::size_t idx, const Container &xs) | |
{ | |
if (size_of_cont(xs) < idx) | |
{ | |
return {}; | |
} | |
auto it = std::begin(xs); | |
internal::advance_iterator(it, idx); | |
return *it; | |
} | |
// API search type: elems_at_idxs : ([Int], [a]) -> [a] | |
// fwd bind count: 1 | |
// Construct a subsequence from the elements with the given indices. | |
// elem_at_idxs([1, 3], [7,6,5,4,3]) == [6, 4] | |
template <typename Container, typename ContainerIdxs, typename T = typename Container::value_type, | |
typename ContainerOut = std::vector<T>> | |
std::vector<T> elems_at_idxs(const ContainerIdxs &idxs, const Container &xs) | |
{ | |
static_assert(std::is_same<typename ContainerIdxs::value_type, std::size_t>::value, "Indices must be std::size_t"); | |
ContainerOut result; | |
internal::prepare_container(result, size_of_cont(idxs)); | |
auto itOut = internal::get_back_inserter(result); | |
for (std::size_t idx : idxs) | |
{ | |
*itOut = elem_at_idx(idx, xs); | |
} | |
return result; | |
} | |
namespace internal | |
{ | |
template <typename Container, typename F> Container transform(internal::reuse_container_t, F f, Container &&xs) | |
{ | |
internal::trigger_static_asserts<internal::unary_function_tag, F, decltype(*std::begin(xs))>(); | |
std::transform(std::begin(xs), std::end(xs), std::begin(xs), f); | |
return std::forward<Container>(xs); | |
} | |
template <typename ContainerOut, typename F, typename ContainerIn> | |
ContainerOut transform(internal::create_new_container_t, F f, const ContainerIn &xs) | |
{ | |
internal::trigger_static_asserts<internal::unary_function_tag, F, decltype(*std::begin(xs))>(); | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it = internal::get_back_inserter<ContainerOut>(ys); | |
std::transform(std::begin(xs), std::end(xs), it, f); | |
return ys; | |
} | |
} // namespace internal | |
// API search type: transform : ((a -> b), [a]) -> [b] | |
// fwd bind count: 1 | |
// Apply a function to every element in a sequence. | |
// transform((*2), [1, 3, 4]) == [2, 6, 8] | |
// Also known as map or fmap. | |
template <typename F, typename ContainerIn, | |
typename ContainerOut = typename internal::same_cont_new_t_from_unary_f< | |
internal::remove_const_and_ref_t<ContainerIn>, F, 0>::type> | |
ContainerOut transform(F f, ContainerIn &&xs) | |
{ | |
using reuse_t = typename std::conditional< | |
std::is_same<internal::can_reuse_v<ContainerIn>, internal::reuse_container_t>::value && | |
std::is_base_of<std::true_type, internal::has_order<ContainerIn>>::value && | |
std::is_same<internal::remove_const_and_ref_t<ContainerIn>, ContainerOut>::value, | |
internal::reuse_container_t, internal::create_new_container_t>::type; | |
return internal::transform<ContainerOut>(reuse_t{}, f, std::forward<ContainerIn>(xs)); | |
} | |
// API search type: transform_convert : ((a -> b), [a]) -> [b] | |
// fwd bind count: 1 | |
// transform_convert((*2), [1, 3, 4]) == [2, 6, 8] | |
// Same as transform, but makes it easy to | |
// use an output container type different from the input container type. | |
template <typename ContainerOut, typename F, typename ContainerIn> | |
ContainerOut transform_convert(F f, const ContainerIn &xs) | |
{ | |
internal::trigger_static_asserts<internal::unary_function_tag, F, typename ContainerIn::value_type>(); | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it = internal::get_back_inserter<ContainerOut>(ys); | |
std::transform(std::begin(xs), std::end(xs), it, f); | |
return ys; | |
} | |
// API search type: transform_inner : ((a -> b), [[a]]) -> [[b]] | |
// fwd bind count: 1 | |
// Applies a function to the elements of the inner containers | |
// of a nested sequence. | |
// transform_inner((*2), [[1, 3, 4], [1, 2]]) == [[2, 6, 8], [2, 4]] | |
// Also known as transform_nested, map_nested or map_inner. | |
template <typename F, typename ContainerIn, | |
typename ContainerOut = typename internal::same_cont_new_t< | |
ContainerIn, | |
typename internal::same_cont_new_t_from_unary_f<typename ContainerIn::value_type, F, 0>::type, 0>::type> | |
ContainerOut transform_inner(F f, const ContainerIn &xs) | |
{ | |
internal::trigger_static_asserts<internal::unary_function_tag, F, typename ContainerIn::value_type::value_type>(); | |
return fplus::transform(fplus::bind_1st_of_2(fplus::transform<F, const typename ContainerIn::value_type &>, f), xs); | |
} | |
namespace internal | |
{ | |
template <typename Container> Container reverse(internal::reuse_container_t, Container &&xs) | |
{ | |
static_assert(internal::has_order<Container>::value, "Reverse: Container has no order."); | |
std::reverse(std::begin(xs), std::end(xs)); | |
return std::forward<Container>(xs); | |
} | |
template <typename Container> Container reverse(internal::create_new_container_t, const Container &xs) | |
{ | |
static_assert(internal::has_order<Container>::value, "Reverse: Container has no order."); | |
Container ys = xs; | |
std::reverse(std::begin(ys), std::end(ys)); | |
return ys; | |
} | |
} // namespace internal | |
// API search type: reverse : [a] -> [a] | |
// fwd bind count: 0 | |
// Reverse a sequence. | |
// reverse([0,4,2,6]) == [6,2,4,0] | |
template <typename Container, typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut reverse(Container &&xs) | |
{ | |
return internal::reverse(internal::can_reuse_v<Container>{}, std::forward<Container>(xs)); | |
} | |
// API search type: take : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Return the first n elements of a sequence xs. | |
// In case n >= length(xs), xs is returned. | |
// take(3, [0,1,2,3,4,5,6,7]) == [0,1,2] | |
// take(10, [0,1,2]) == [0,1,2] | |
template <typename Container, typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut take(std::size_t amount, Container &&xs) | |
{ | |
if (amount >= size_of_cont(xs)) | |
return xs; | |
return get_segment(0, amount, std::forward<Container>(xs)); | |
} | |
// API search type: take_exact : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Return exactly the first n elements of a sequence xs. | |
// Unsafe! Crashes then sequence is too short. | |
// take_exact(3, [0,1,2,3,4,5,6,7]) == [0,1,2] | |
// take_exact(10, [0,1,2]) == crash | |
template <typename Container, typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut take_exact(std::size_t amount, Container &&xs) | |
{ | |
return get_segment(0, amount, std::forward<Container>(xs)); | |
} | |
// API search type: take_cyclic : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Takes n elements from a sequence considering it as cyclic. | |
// take_cyclic(5, [0,1,2,3]) == [0,1,2,3,0] | |
// take_cyclic(7, [0,1,2,3]) == [0,1,2,3,0,1,2] | |
// take_cyclic(7, [0,1]) == [0,1,0,1,0,1,0] | |
// take_cyclic(2, [0,1,2,3]) == [0,1] | |
// take_cyclic(3, [0]) == [0,0,0] | |
// take_cyclic(3, []) == crash! | |
// Also known as take_wrap. | |
// xs must be non-empty. | |
template <typename Container> Container take_cyclic(std::size_t amount, const Container &xs) | |
{ | |
assert(!xs.empty()); | |
Container ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it_out = internal::get_back_inserter(ys); | |
auto it_in = std::begin(xs); | |
while (amount != 0) | |
{ | |
*it_out = *it_in; | |
--amount; | |
++it_in; | |
if (it_in == std::end(xs)) | |
{ | |
it_in = std::begin(xs); | |
} | |
} | |
return ys; | |
} | |
// API search type: drop : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Skip the first n elements of a sequence xs. | |
// If n > length(xs) an empty sequence is returned. | |
// drop(3, [0,1,2,3,4,5,6,7]) == [3,4,5,6,7] | |
// Also known as skip. | |
template <typename Container, typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut drop(std::size_t amount, Container &&xs) | |
{ | |
if (amount >= size_of_cont(xs)) | |
return ContainerOut(); | |
return get_segment(amount, size_of_cont(xs), std::forward<Container>(xs)); | |
} | |
// API search type: take_last : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Return the last n elements of a sequence xs. | |
// In case n >= length(xs), xs is returned. | |
// take_last(3, [0,1,2,3,4,5,6,7]) == [5,6,7] | |
// take_last(10, [0,1,2]) == [0,1,2] | |
template <typename Container, typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut take_last(std::size_t amount, Container &&xs) | |
{ | |
if (amount >= size_of_cont(xs)) | |
return xs; | |
return drop(size_of_cont(xs) - amount, std::forward<Container>(xs)); | |
} | |
// API search type: drop_last : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Skip the last n elements of a sequence xs. | |
// If n > length(xs) an empty sequence is returned. | |
// drop_last(3, [0,1,2,3,4,5,6,7]) == [0,1,2,3,4] | |
template <typename Container, typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut drop_last(std::size_t amount, Container &&xs) | |
{ | |
if (amount >= size_of_cont(xs)) | |
return ContainerOut(); | |
return take(size_of_cont(xs) - amount, std::forward<Container>(xs)); | |
} | |
// API search type: drop_exact : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Skip exactly the first n elements of a sequence xs. | |
// Unsafe! Crashes when xs is too short. | |
// drop_exact(3, [0,1,2,3,4,5,6,7]) == [3,4,5,6,7] | |
// drop_exact(10, [0,1,2,3,4,5,6,7]) == crash | |
template <typename Container, typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut drop_exact(std::size_t amount, Container &&xs) | |
{ | |
return get_segment(amount, size_of_cont(xs), std::forward<Container>(xs)); | |
} | |
// API search type: take_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Take elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// take_while(is_even, [0,2,4,5,6,7,8]) == [0,2,4] | |
template <typename Container, typename UnaryPredicate> Container take_while(UnaryPredicate pred, const Container &xs) | |
{ | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
auto itFirst = std::find_if(std::begin(xs), std::end(xs), logical_not(pred)); | |
if (itFirst == std::end(xs)) | |
return xs; | |
return Container(std::begin(xs), itFirst); | |
} | |
// API search type: take_last_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Take elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// take_last_while(is_even, [0,2,7,5,6,4,8]) == [6,4,8] | |
template <typename Container, typename UnaryPredicate> | |
Container take_last_while(UnaryPredicate pred, const Container &xs) | |
{ | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
const auto r_begin = internal::make_reverse_iterator(std::end(xs)); | |
const auto r_end = internal::make_reverse_iterator(std::begin(xs)); | |
const auto itFirstReverse = std::find_if(r_begin, r_end, logical_not(pred)); | |
if (itFirstReverse == r_begin) | |
return Container(); | |
if (itFirstReverse == r_end) | |
return xs; | |
return Container(itFirstReverse.base(), std::end(xs)); | |
} | |
// API search type: drop_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Remove elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// drop_while(is_even, [0,2,4,5,6,7,8]) == [5,6,7,8] | |
// Also known as trim_left_by. | |
template <typename Container, typename UnaryPredicate> Container drop_while(UnaryPredicate pred, const Container &xs) | |
{ | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
auto itFirstNot = std::find_if_not(std::begin(xs), std::end(xs), pred); | |
if (itFirstNot == std::end(xs)) | |
return Container(); | |
return Container(itFirstNot, std::end(xs)); | |
} | |
// API search type: drop_last_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Remove elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// drop_last_while(is_even, [0,2,7,5,6,4,8]) == [0,2,7,5] | |
template <typename Container, typename UnaryPredicate> | |
Container drop_last_while(UnaryPredicate pred, const Container &xs) | |
{ | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
const auto r_begin = internal::make_reverse_iterator(std::end(xs)); | |
const auto r_end = internal::make_reverse_iterator(std::begin(xs)); | |
const auto itFirstNotReverse = std::find_if_not(r_begin, r_end, pred); | |
if (itFirstNotReverse == r_begin) | |
return xs; | |
if (itFirstNotReverse == r_end) | |
return Container(); | |
return Container(std::begin(xs), itFirstNotReverse.base()); | |
} | |
// API search type: fold_left : (((a, b) -> a), a, [b]) -> a | |
// fwd bind count: 2 | |
// fold_left((+), 0, [1, 2, 3]) == ((0+1)+2)+3 == 6 | |
// Takes the second argument and the first item of the list | |
// and applies the function to them, | |
// then feeds the function with this result and the second argument and so on. | |
template <typename F, typename Container, typename Acc> Acc fold_left(F f, const Acc &init, const Container &xs) | |
{ | |
using std::begin; | |
using std::end; | |
return internal::accumulate(begin(xs), end(xs), init, f); | |
} | |
// API search type: reduce : (((a, a) -> a), a, [a]) -> a | |
// fwd bind count: 2 | |
// reduce((+), 0, [1, 2, 3]) == (0+1+2+3) == 6 | |
// Combines the initial value and all elements of the sequence | |
// using the given function. | |
// The set of f, init and value_type should form a monoid. | |
template <typename F, typename Container> | |
typename Container::value_type reduce(F f, const typename Container::value_type &init, const Container &xs) | |
{ | |
return fold_left(f, init, xs); | |
} | |
// API search type: fold_left_1 : (((a, a) -> a), [a]) -> a | |
// fwd bind count: 1 | |
// fold_left_1((+), [1, 2, 3]) == (1+2)+3 == 6 | |
// Takes the first 2 items of the list and applies the function to them, | |
// then feeds the function with this result and the third argument and so on. | |
// xs must be non-empty. | |
template <typename F, typename Container> auto fold_left_1(F f, const Container &xs) | |
{ | |
assert(!xs.empty()); | |
using std::begin; | |
using std::end; | |
const auto it = begin(xs); | |
return internal::accumulate(std::next(it), end(xs), *it, f); | |
} | |
// API search type: reduce_1 : (((a, a) -> a), [a]) -> a | |
// fwd bind count: 1 | |
// reduce_1((+), [1, 2, 3]) == (1+2+3) == 6 | |
// Joins all elements of the sequence using the given function. | |
// The set of f and value_type should form a semigroup. | |
template <typename F, typename Container> typename Container::value_type reduce_1(F f, const Container &xs) | |
{ | |
assert(!xs.empty()); | |
return fold_left_1(f, xs); | |
} | |
// API search type: fold_right : (((a, b) -> b), b) -> [a] -> b | |
// fwd bind count: 2 | |
// fold_right((+), 0, [1, 2, 3]) == 1+(2+(3+0)) == 6 | |
// Takes the second argument and the last item of the list | |
// and applies the function, | |
// then it takes the penultimate item from the end and the result, and so on. | |
template <typename F, typename Container, typename Acc> Acc fold_right(F f, const Acc &init, const Container &xs) | |
{ | |
return internal::accumulate(xs.rbegin(), xs.rend(), init, flip(f)); | |
} | |
// API search type: fold_right_1 : (((a, a) -> a), [a]) -> a | |
// fwd bind count: 1 | |
// fold_right_1((+), [1, 2, 3]) == 1+(2+3)) == 6 | |
// Takes the last two items of the list and applies the function, | |
// then it takes the third item from the end and the result, and so on. | |
template <typename F, typename Container> auto fold_right_1(F f, const Container &xs) | |
{ | |
assert(!xs.empty()); | |
const auto it = xs.rbegin(); | |
return internal::accumulate(std::next(it), xs.rend(), *it, flip(f)); | |
} | |
// API search type: scan_left : (((a, b) -> a), a, [b]) -> [a] | |
// fwd bind count: 2 | |
// scan_left((+), 0, [1, 2, 3]) == [0, 1, 3, 6] | |
// Takes the second argument and the first item of the list | |
// and applies the function to them, | |
// then feeds the function with this result and the second argument and so on. | |
// It returns the list of intermediate and final results. | |
template <typename F, typename ContainerIn, typename Acc> auto scan_left(F f, const Acc &init, const ContainerIn &xs) | |
{ | |
using ContainerOut = typename internal::same_cont_new_t<ContainerIn, Acc, 1>::type; | |
ContainerOut result; | |
internal::prepare_container(result, size_of_cont(xs) + 1); | |
using std::begin; | |
using std::end; | |
internal::scan_impl(f, init, internal::get_back_inserter(result), begin(xs), end(xs)); | |
return result; | |
} | |
// API search type: scan_left_1 : (((a, a) -> a), [a]) -> [a] | |
// fwd bind count: 1 | |
// scan_left_1((+), [1, 2, 3]) == [1, 3, 6] | |
// Takes the first 2 items of the list and applies the function to them, | |
// then feeds the function with this result and the third argument and so on. | |
// It returns the list of intermediate and final results. | |
// xs must be non-empty. | |
template <typename F, typename ContainerIn> auto scan_left_1(F f, const ContainerIn &xs) | |
{ | |
assert(!xs.empty()); | |
using std::begin; | |
using std::end; | |
const auto beginIt = begin(xs); | |
using ContainerOut = | |
typename internal::same_cont_new_t<ContainerIn, internal::uncvref_t<decltype(*beginIt)>, 0>::type; | |
ContainerOut result; | |
internal::prepare_container(result, size_of_cont(xs)); | |
internal::scan_impl(f, *beginIt, internal::get_back_inserter(result), std::next(beginIt), end(xs)); | |
return result; | |
} | |
// API search type: scan_right : (((a, b) -> b), b, [a]) -> [b] | |
// fwd bind count: 2 | |
// scan_right((+), 0, [1, 2, 3]) == [6, 5, 3, 0] | |
// Takes the second argument and the last item of the list | |
// and applies the function, | |
// then it takes the penultimate item from the end and the result, and so on. | |
// It returns the list of intermediate and final results. | |
template <typename F, typename ContainerIn, typename Acc = typename utils::function_traits<F>::template arg<1>::type, | |
typename ContainerOut = typename internal::same_cont_new_t<ContainerIn, Acc, 1>::type> | |
ContainerOut scan_right(F f, const Acc &init, const ContainerIn &xs) | |
{ | |
return reverse(scan_left(flip(f), init, reverse(xs))); | |
} | |
// API search type: scan_right_1 : (((a, a) -> a), [a]) -> [a] | |
// fwd bind count: 1 | |
// scan_right_1((+), [1, 2, 3]) == [6, 5, 3] | |
// Takes the last two items of the list and applies the function, | |
// then it takes the third item from the end and the result, and so on. | |
// It returns the list of inntermediate and final results. | |
template <typename F, typename ContainerIn, typename Acc = typename ContainerIn::value_type, | |
typename ContainerOut = typename internal::same_cont_new_t<ContainerIn, Acc, 0>::type> | |
ContainerOut scan_right_1(F f, const ContainerIn &xs) | |
{ | |
return reverse(scan_left_1(flip(f), reverse(xs))); | |
} | |
// API search type: sum : [a] -> a | |
// fwd bind count: 0 | |
// Adds up all values in a sequence. | |
// sum([0,3,1]) == 4 | |
// sum([]) == 0 | |
template <typename Container, typename T = typename Container::value_type> T sum(const Container &xs) | |
{ | |
T result = T(); | |
for (const auto &x : xs) | |
{ | |
result = result + x; | |
} | |
return result; | |
} | |
// API search type: product : [a] -> a | |
// fwd bind count: 0 | |
// Returns the product of all values in a sequence. | |
// product([3,1,2]) == 6 | |
// product([]) == 1 | |
template <typename Container, typename T = typename Container::value_type> T product(const Container &xs) | |
{ | |
T result{1}; | |
for (const auto &x : xs) | |
{ | |
result = result * x; | |
} | |
return result; | |
} | |
namespace internal | |
{ | |
template <typename T, typename Container> Container append_elem(internal::reuse_container_t, const T &y, Container &&xs) | |
{ | |
*internal::get_back_inserter(xs) = y; | |
return std::forward<Container>(xs); | |
} | |
template <typename T, typename Container> | |
Container append_elem(internal::create_new_container_t, const T &y, const Container &xs) | |
{ | |
Container result; | |
internal::prepare_container(result, size_of_cont(xs) + 1); | |
std::copy(std::begin(xs), std::end(xs), internal::get_back_inserter(result)); | |
*internal::get_back_inserter(result) = y; | |
return result; | |
} | |
} // namespace internal | |
// API search type: append_elem : (a, [a]) -> [a] | |
// fwd bind count: 1 | |
// Extends a sequence with one element at the back. | |
// append_elem([1, 2], 3) == [1, 2, 3] | |
template <typename Container, typename ContainerOut = internal::remove_const_and_ref_t<Container>, | |
typename T = typename ContainerOut::value_type> | |
ContainerOut append_elem(const T &y, Container &&xs) | |
{ | |
return internal::append_elem(internal::can_reuse_v<Container>{}, y, std::forward<Container>(xs)); | |
} | |
namespace internal | |
{ | |
template <typename T> std::list<T> prepend_elem(internal::reuse_container_t, const T &y, std::list<T> &&xs) | |
{ | |
xs.push_front(y); | |
return std::forward<std::list<T>>(xs); | |
} | |
template <typename T, typename Container> | |
Container prepend_elem(internal::reuse_container_t, const T &y, Container &&xs) | |
{ | |
xs.resize(size_of_cont(xs) + 1); | |
std::copy(++xs.rbegin(), xs.rend(), xs.rbegin()); | |
*std::begin(xs) = y; | |
return std::forward<Container>(xs); | |
} | |
template <typename T, typename Container> | |
Container prepend_elem(internal::create_new_container_t, const T &y, const Container &xs) | |
{ | |
Container result; | |
internal::prepare_container(result, size_of_cont(xs) + 1); | |
*internal::get_back_inserter(result) = y; | |
std::copy(std::begin(xs), std::end(xs), internal::get_back_inserter(result)); | |
return result; | |
} | |
} // namespace internal | |
// API search type: prepend_elem : (a, [a]) -> [a] | |
// fwd bind count: 1 | |
// Extends a sequence with one element in the front. | |
// prepend_elem([2, 3], 1) == [1, 2, 3] | |
template <typename Container, typename ContainerOut = internal::remove_const_and_ref_t<Container>, | |
typename T = typename ContainerOut::value_type> | |
ContainerOut prepend_elem(const T &y, Container &&xs) | |
{ | |
return internal::prepend_elem(internal::can_reuse_v<Container>{}, y, std::forward<Container>(xs)); | |
} | |
// API search type: append : ([a], [a]) -> [a] | |
// fwd bind count: 1 | |
// Concatenates two sequences. | |
// append([1, 2], [3, 4, 5]) == [1, 2, 3, 4, 5] | |
template <typename ContainerIn1, typename ContainerIn2 = ContainerIn1, typename ContainerOut = ContainerIn1> | |
ContainerOut append(const ContainerIn1 &xs, const ContainerIn2 &ys) | |
{ | |
ContainerOut result; | |
internal::prepare_container(result, size_of_cont(xs) + size_of_cont(ys)); | |
std::copy(std::begin(xs), std::end(xs), internal::get_back_inserter(result)); | |
std::copy(std::begin(ys), std::end(ys), internal::get_back_inserter(result)); | |
return result; | |
} | |
// API search type: append_convert : ([a], [a]) -> [a] | |
// fwd bind count: 1 | |
// Same as append, but makes it easier to | |
// use an output container type different from the input container type. | |
template <typename ContainerOut, typename ContainerIn1, typename ContainerIn2 = ContainerIn1> | |
ContainerOut append_convert(const ContainerIn1 &xs, const ContainerIn2 &ys) | |
{ | |
return append<ContainerIn1, ContainerIn2, ContainerOut>(xs, ys); | |
} | |
// API search type: concat : [[a]] -> [a] | |
// fwd bind count: 0 | |
// Concatenates multiple sequences. | |
// concat([[1, 2], [], [3]]) == [1, 2, 3] | |
// Also known as flatten. | |
template <typename ContainerIn, typename ContainerOut = typename ContainerIn::value_type> | |
ContainerOut concat(const ContainerIn &xss) | |
{ | |
std::size_t length = sum(transform(size_of_cont<typename ContainerIn::value_type>, xss)); | |
ContainerOut result; | |
internal::prepare_container(result, length); | |
using std::begin; | |
using std::end; | |
for (const auto &xs : xss) | |
{ | |
result.insert(end(result), begin(xs), end(xs)); | |
} | |
return result; | |
} | |
// API search type: interweave : ([a], [a]) -> [a] | |
// fwd bind count: 1 | |
// Return a sequence that contains elements from the two provided sequences | |
// in alternating order. If one list runs out of items, | |
// appends the items from the remaining list. | |
// interweave([1,3], [2,4]) == [1,2,3,4] | |
// interweave([1,3,5,7], [2,4]) == [1,2,3,4,5,7] | |
// See interleave for interweaving more than two sequences. | |
template <typename Container> Container interweave(const Container &xs, const Container &ys) | |
{ | |
Container result; | |
internal::prepare_container(result, size_of_cont(xs) + size_of_cont(ys)); | |
auto it = internal::get_back_inserter<Container>(result); | |
auto it_xs = std::begin(xs); | |
auto it_ys = std::begin(ys); | |
while (it_xs != std::end(xs) || it_ys != std::end(ys)) | |
{ | |
if (it_xs != std::end(xs)) | |
*it = *(it_xs++); | |
if (it_ys != std::end(ys)) | |
*it = *(it_ys++); | |
} | |
return result; | |
} | |
// API search type: unweave : [a] -> ([a], [a]) | |
// fwd bind count: 0 | |
// Puts the elements with an even index into the first list, | |
// and the elements with an odd index into the second list. | |
// Inverse of interweave. | |
// unweave([0,1,2,3]) == ([0,2], [1,3]) | |
// unweave([0,1,2,3,4]) == ([0,2,4], [1,3]) | |
template <typename Container> std::pair<Container, Container> unweave(const Container &xs) | |
{ | |
std::pair<Container, Container> result; | |
if (is_even(size_of_cont(xs))) | |
internal::prepare_container(result.first, size_of_cont(xs) / 2); | |
else | |
internal::prepare_container(result.first, size_of_cont(xs) / 2 + 1); | |
internal::prepare_container(result.second, size_of_cont(xs) / 2); | |
auto it_even = internal::get_back_inserter<Container>(result.first); | |
auto it_odd = internal::get_back_inserter<Container>(result.second); | |
std::size_t counter = 0; | |
for (const auto &x : xs) | |
{ | |
if (counter++ % 2 == 0) | |
*it_even = x; | |
else | |
*it_odd = x; | |
} | |
return result; | |
} | |
namespace internal | |
{ | |
template <typename Compare, typename T> | |
std::list<T> sort_by(internal::reuse_container_t, Compare comp, std::list<T> &&xs) | |
{ | |
xs.sort(comp); | |
return std::forward<std::list<T>>(xs); | |
} | |
template <typename Compare, typename T> | |
std::list<T> sort_by(internal::create_new_container_t, Compare comp, const std::list<T> &xs) | |
{ | |
auto result = xs; | |
result.sort(comp); | |
return result; | |
} | |
template <typename Compare, typename Container> | |
Container sort_by(internal::reuse_container_t, Compare comp, Container &&xs) | |
{ | |
std::sort(std::begin(xs), std::end(xs), comp); | |
return std::forward<Container>(xs); | |
} | |
template <typename Compare, typename Container> | |
Container sort_by(internal::create_new_container_t, Compare comp, const Container &xs) | |
{ | |
auto result = xs; | |
std::sort(std::begin(result), std::end(result), comp); | |
return result; | |
} | |
} // namespace internal | |
// API search type: sort_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence by given less comparator. | |
template <typename Compare, typename Container, typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut sort_by(Compare comp, Container &&xs) | |
{ | |
return internal::sort_by(internal::can_reuse_v<Container>{}, comp, std::forward<Container>(xs)); | |
} | |
namespace internal | |
{ | |
// workarounds for clang bug 24115 | |
// (std::sort and std::unique with std::function as comp) | |
// https://llvm.org/bugs/show_bug.cgi?id=24115 | |
template <typename F> struct is_less_by_struct | |
{ | |
is_less_by_struct(F f) : f_(f){}; | |
template <typename T> bool operator()(const T &x, const T &y) | |
{ | |
return f_(x) < f_(y); | |
} | |
private: | |
F f_; | |
}; | |
template <typename F> struct is_equal_by_struct | |
{ | |
is_equal_by_struct(F f) : f_(f){}; | |
template <typename T> bool operator()(const T &x, const T &y) | |
{ | |
return f_(x) == f_(y); | |
} | |
private: | |
F f_; | |
}; | |
} // namespace internal | |
// API search type: sort_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence by a given transformer. | |
template <typename F, typename Container, typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut sort_on(F f, Container &&xs) | |
{ | |
return sort_by(internal::is_less_by_struct<F>(f), std::forward<Container>(xs)); | |
} | |
// API search type: sort : [a] -> [a] | |
// fwd bind count: 0 | |
// Sort a sequence to ascending order using std::less. | |
template <typename Container, typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut sort(Container &&xs) | |
{ | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return sort_by(std::less<T>(), std::forward<Container>(xs)); | |
} | |
namespace internal | |
{ | |
template <typename Compare, typename T> | |
std::list<T> stable_sort_by(internal::reuse_container_t, Compare comp, std::list<T> &&xs) | |
{ | |
xs.sort(comp); // std::list<T>::sort ist already stable. | |
return std::forward<std::list<T>>(xs); | |
} | |
template <typename Compare, typename T> | |
std::list<T> stable_sort_by(internal::create_new_container_t, Compare comp, const std::list<T> &xs) | |
{ | |
auto result = xs; | |
result.sort(comp); // std::list<T>::sort ist already stable. | |
return result; | |
} | |
template <typename Compare, typename Container> | |
Container stable_sort_by(internal::reuse_container_t, Compare comp, Container &&xs) | |
{ | |
std::sort(std::begin(xs), std::end(xs), comp); | |
return std::forward<Container>(xs); | |
} | |
template <typename Compare, typename Container> | |
Container stable_sort_by(internal::create_new_container_t, Compare comp, const Container &xs) | |
{ | |
auto result = xs; | |
std::sort(std::begin(result), std::end(result), comp); | |
return result; | |
} | |
} // namespace internal | |
// API search type: stable_sort_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence stably by given less comparator. | |
template <typename Compare, typename Container, typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut stable_sort_by(Compare comp, Container &&xs) | |
{ | |
return internal::stable_sort_by(internal::can_reuse_v<Container>{}, comp, std::forward<Container>(xs)); | |
} | |
// API search type: stable_sort_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence stably by given transformer. | |
template <typename F, typename Container, typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut stable_sort_on(F f, Container &&xs) | |
{ | |
return stable_sort_by(internal::is_less_by_struct<F>(f), std::forward<Container>(xs)); | |
} | |
// API search type: stable_sort : [a] -> [a] | |
// fwd bind count: 0 | |
// Sort a sequence stably to ascending order using std::less. | |
template <typename Container, typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut stable_sort(Container &&xs) | |
{ | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return stable_sort_by(std::less<T>(), std::forward<Container>(xs)); | |
} | |
namespace internal | |
{ | |
template <typename Compare, typename Container> | |
Container partial_sort_by(internal::reuse_container_t, Compare comp, std::size_t count, Container &&xs) | |
{ | |
if (count > xs.size()) | |
{ | |
count = xs.size(); | |
} | |
auto middle = std::begin(xs); | |
internal::advance_iterator(middle, count); | |
std::partial_sort(std::begin(xs), middle, std::end(xs), comp); | |
return std::forward<Container>(get_segment(internal::reuse_container_t(), 0, count, xs)); | |
} | |
template <typename Compare, typename Container> | |
Container partial_sort_by(internal::create_new_container_t, Compare comp, std::size_t count, const Container &xs) | |
{ | |
auto result = xs; | |
return partial_sort_by(internal::reuse_container_t(), comp, count, std::move(result)); | |
} | |
} // namespace internal | |
// API search type: partial_sort_by : (((a, a) -> Bool), Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Partially sort a sequence by a given less comparator. | |
// Returns only the sorted segment. | |
template <typename Compare, typename Container, typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut partial_sort_by(Compare comp, std::size_t count, Container &&xs) | |
{ | |
return internal::partial_sort_by(internal::can_reuse_v<Container>{}, comp, count, std::forward<Container>(xs)); | |
} | |
// API search type: partial_sort_on : ((a -> b), Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Partially sort a sequence by a given transformer. | |
// Returns only the sorted segment. | |
template <typename F, typename Container, typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut partial_sort_on(F f, std::size_t count, Container &&xs) | |
{ | |
return partial_sort_by(internal::is_less_by_struct<F>(f), count, std::forward<Container>(xs)); | |
} | |
// API search type: partial_sort : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Partially sort a sequence in ascending order using std::less. | |
// Returns only the sorted segment. | |
template <typename Container, typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut partial_sort(std::size_t count, Container &&xs) | |
{ | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return partial_sort_by(std::less<T>(), count, std::forward<Container>(xs)); | |
} | |
// API search type: nth_element_by : (((a, a) -> Bool), Int, [a]) -> a | |
// fwd bind count: 2 | |
// Return the nth largest element of a sequence by a given less comparator. | |
template <typename Compare, typename Container, typename T = typename Container::value_type> | |
T nth_element_by(Compare comp, std::size_t n, const Container &xs) | |
{ | |
assert(n < xs.size()); | |
auto result = xs; | |
auto middle = std::begin(result); | |
internal::advance_iterator(middle, n); | |
std::nth_element(std::begin(result), middle, std::end(result), comp); | |
return *middle; | |
} | |
// API search type: nth_element_on : ((a -> b), Int, [a]) -> a | |
// fwd bind count: 2 | |
// Return the nth largest element of a sequence by a given transformer. | |
template <typename F, typename Container, typename T = typename Container::value_type> | |
T nth_element_on(F f, std::size_t n, const Container &xs) | |
{ | |
return nth_element_by(internal::is_less_by_struct<F>(f), n, xs); | |
} | |
// API search type: nth_element : (Int, [a]) -> a | |
// fwd bind count: 1 | |
// Return the nth largest element of a sequence using std::less. | |
template <typename Container, typename T = typename Container::value_type> | |
T nth_element(std::size_t n, const Container &xs) | |
{ | |
return nth_element_by(std::less<T>(), n, xs); | |
} | |
namespace internal | |
{ | |
template <typename BinaryPredicate, typename Container> | |
Container unique_by(internal::reuse_container_t, BinaryPredicate pred, Container &&xs) | |
{ | |
internal::check_binary_predicate_for_container<BinaryPredicate, Container>(); | |
const auto it_end = std::unique(std::begin(xs), std::end(xs), pred); | |
xs.erase(it_end, std::end(xs)); | |
return std::forward<Container>(xs); | |
} | |
template <typename BinaryPredicate, typename Container> | |
Container unique_by(internal::create_new_container_t, BinaryPredicate pred, const Container &xs) | |
{ | |
auto result = xs; | |
return unique_by(internal::reuse_container_t(), pred, std::move(result)); | |
} | |
} // namespace internal | |
// API search type: unique_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Like unique, eliminates all but the first element | |
// from every consecutive group of equivalent elements from the sequence; | |
// but with a user supplied equality predicate. | |
// See nub_by for making elements globally unique in a sequence. | |
// O(n) | |
template <typename BinaryPredicate, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut unique_by(BinaryPredicate pred, Container &&xs) | |
{ | |
return internal::unique_by(internal::can_reuse_v<Container>{}, pred, std::forward<Container>(xs)); | |
} | |
// API search type: unique_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Like unique, eliminates all but the first element | |
// from every consecutive group of equivalent elements from the sequence; | |
// but with a user supplied transformation (e.g. getter). | |
// See nub_on for making elements globally unique in a sequence. | |
// O(n) | |
// Also known as drop_repeats. | |
template <typename F, typename Container, typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut unique_on(F f, Container &&xs) | |
{ | |
return unique_by(internal::is_equal_by_struct<F>(f), std::forward<Container>(xs)); | |
} | |
// API search type: unique : [a] -> [a] | |
// fwd bind count: 0 | |
// Eliminates all but the first element | |
// from every consecutive group of equivalent elements from the sequence. | |
// unique([1,2,2,3,2]) == [1,2,3,2] | |
// See nub for making elements globally unique in a sequence. | |
// O(n) | |
template <typename Container, typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut unique(Container &&xs) | |
{ | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return unique_on(identity<T>, std::forward<Container>(xs)); | |
} | |
// API search type: intersperse : (a, [a]) -> [a] | |
// fwd bind count: 1 | |
// Insert a value between all adjacent values in a sequence. | |
// intersperse(0, [1, 2, 3]) == [1, 0, 2, 0, 3] | |
// Also known as interpose. | |
template <typename Container, typename X = typename Container::value_type> | |
Container intersperse(const X &value, const Container &xs) | |
{ | |
if (xs.empty()) | |
return Container(); | |
if (size_of_cont(xs) == 1) | |
return xs; | |
Container result; | |
internal::prepare_container(result, std::max<std::size_t>(0, size_of_cont(xs) * 2 - 1)); | |
auto it = internal::get_back_inserter(result); | |
for_each(std::begin(xs), --std::end(xs), [&value, &it](const X &x) { | |
*it = x; | |
*it = value; | |
}); | |
*it = xs.back(); | |
return result; | |
} | |
// API search type: join : ([a], [[a]]) -> [a] | |
// fwd bind count: 1 | |
// Inserts a separator sequence in between the elements | |
// of a sequence of sequences and concatenates the result. | |
// Also known as intercalate or implode. | |
// join(", ", ["a", "bee", "cee"]) == "a, bee, cee" | |
// join([0, 0], [[1], [2], [3, 4]]) == [1, 0, 0, 2, 0, 0, 3, 4] | |
template <typename Container, typename X = typename Container::value_type> | |
X join(const X &separator, const Container &xs) | |
{ | |
return concat(intersperse(separator, xs)); | |
} | |
// API search type: join_elem : (a, [[a]]) -> [a] | |
// fwd bind count: 1 | |
// Inserts a separator in between the elements | |
// of a sequence of sequences and concatenates the result. | |
// Also known as intercalate_elem. | |
// join_elem(';', ["a", "bee", "cee"]) == "a;bee;cee" | |
// join_elem(0, [[1], [2], [3, 4]]) == [1, 0, 2, 0, 3, 4]] | |
template <typename Container, typename Inner = typename Container::value_type, typename X = typename Inner::value_type> | |
Inner join_elem(const X &separator, const Container &xs) | |
{ | |
return join(Inner(1, separator), xs); | |
} | |
// API search type: is_elem_of_by : ((a -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if at least one element of the container fulfils a predicate. | |
// is_elem_of_by((==), [1,2,3]) == true | |
template <typename UnaryPredicate, typename Container> bool is_elem_of_by(UnaryPredicate pred, const Container &xs) | |
{ | |
return std::find_if(std::begin(xs), std::end(xs), pred) != std::end(xs); | |
} | |
// API search type: is_elem_of : (a, [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if an element is a member of a container. | |
// is_elem_of(2, [1,2,3]) == true | |
// Also known as flip(contains). | |
template <typename Container> bool is_elem_of(const typename Container::value_type &x, const Container &xs) | |
{ | |
return is_elem_of_by(is_equal_to(x), xs); | |
} | |
// API search type: nub_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Makes the elements in a container unique with respect to a predicate | |
// nub_by((==), [1,2,2,3,2]) == [1,2,3] | |
// O(n^2) | |
template <typename Container, typename BinaryPredicate> Container nub_by(BinaryPredicate p, const Container &xs) | |
{ | |
Container result; | |
auto itOut = internal::get_back_inserter(result); | |
for (const auto &x : xs) | |
{ | |
auto eqToX = bind_1st_of_2(p, x); | |
if (!is_elem_of_by(eqToX, result)) | |
{ | |
*itOut = x; | |
} | |
} | |
return result; | |
} | |
// API search type: nub_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Makes the elements in a container unique | |
// with respect to their function value. | |
// nub_on((mod 10), [12,32,15]) == [12,15] | |
// O(n^2) | |
template <typename Container, typename F> Container nub_on(F f, const Container &xs) | |
{ | |
return nub_by(is_equal_by(f), xs); | |
} | |
// API search type: nub : [a] -> [a] | |
// fwd bind count: 0 | |
// Makes the elements in a container unique. | |
// nub([1,2,2,3,2]) == [1,2,3] | |
// O(n^2) | |
// Also known as distinct. | |
template <typename Container> Container nub(const Container &xs) | |
{ | |
typedef typename Container::value_type T; | |
return nub_by(std::equal_to<T>(), xs); | |
} | |
// API search type: all_unique_by_eq : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all elements in a container are unique | |
// with respect to a predicate. | |
// Returns true for empty containers. | |
// O(n^2) | |
template <typename Container, typename BinaryPredicate> bool all_unique_by_eq(BinaryPredicate p, const Container &xs) | |
{ | |
internal::check_binary_predicate_for_container<BinaryPredicate, Container>(); | |
return size_of_cont(nub_by(p, xs)) == size_of_cont(xs); | |
} | |
// API search type: all_unique_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all elements in a container are unique | |
// with respect to their function values. | |
// Returns true for empty containers. | |
// O(n^2) | |
template <typename Container, typename F> bool all_unique_on(F f, const Container &xs) | |
{ | |
return all_unique_by_eq(is_equal_by(f), xs); | |
} | |
// API search type: all_unique : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if all elements in a container are unique. | |
// Returns true for empty containers. | |
// O(n^2) | |
template <typename Container> bool all_unique(const Container &xs) | |
{ | |
typedef typename Container::value_type T; | |
auto comp = std::equal_to<T>(); | |
return all_unique_by_eq(comp, xs); | |
} | |
// API search type: is_strictly_sorted_by : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is strictly sorted using a predicate. | |
// comp(a, b) must return true only if a < b. | |
// O(n) | |
template <typename Container, typename Compare> bool is_strictly_sorted_by(Compare comp, const Container &xs) | |
{ | |
internal::check_compare_for_container<Compare, Container>(); | |
if (size_of_cont(xs) < 2) | |
return true; | |
auto it1 = std::begin(xs); | |
for (auto it2 = it1 + 1; it2 < std::end(xs); ++it1, ++it2) | |
if (!internal::invoke(comp, *it1, *it2)) | |
return false; | |
return true; | |
} | |
// API search type: is_strictly_sorted_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is strictly sorted using a transformer. | |
// O(n) | |
template <typename Container, typename F> bool is_strictly_sorted_on(F f, const Container &xs) | |
{ | |
return is_strictly_sorted_by(is_less_by(f), xs); | |
} | |
// API search type: is_strictly_sorted : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if a container already is strictly sorted | |
// in ascending order using std::less. | |
// O(n) | |
template <typename Container> bool is_strictly_sorted(const Container &xs) | |
{ | |
typedef typename Container::value_type T; | |
auto comp = std::less<T>(); | |
return is_strictly_sorted_by(comp, xs); | |
} | |
// API search type: is_sorted_by : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is sorted using a predicate. | |
// comp(a, b) must return true only if a < b. | |
// O(n) | |
template <typename Container, typename Compare> bool is_sorted_by(Compare comp, const Container &xs) | |
{ | |
internal::check_compare_for_container<Compare, Container>(); | |
if (size_of_cont(xs) < 2) | |
return true; | |
auto it1 = std::begin(xs); | |
for (auto it2 = it1 + 1; it2 < std::end(xs); ++it1, ++it2) | |
if (internal::invoke(comp, *it2, *it1)) | |
return false; | |
return true; | |
} | |
// API search type: is_sorted_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is strictly sorted using a transformer. | |
// O(n) | |
template <typename Container, typename F> bool is_sorted_on(F f, const Container &xs) | |
{ | |
return is_sorted_by(is_less_by(f), xs); | |
} | |
// API search type: is_sorted : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if a container already is sorted | |
// in ascending order using std::less. | |
// O(n) | |
template <typename Container> bool is_sorted(const Container &xs) | |
{ | |
typedef typename Container::value_type T; | |
auto comp = std::less<T>(); | |
return is_sorted_by(comp, xs); | |
} | |
// API search type: is_prefix_of : ([a], [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a containers starts with a token. | |
// is_prefix_of("Fun", "FunctionalPlus") == true | |
template <typename Container> bool is_prefix_of(const Container &token, const Container &xs) | |
{ | |
if (size_of_cont(token) > size_of_cont(xs)) | |
return false; | |
return get_segment(0, size_of_cont(token), xs) == token; | |
} | |
// API search type: is_suffix_of : ([a], [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a containers contains a token as a segment. | |
// is_suffix_of("us", "FunctionalPlus") == true | |
template <typename Container> bool is_suffix_of(const Container &token, const Container &xs) | |
{ | |
if (size_of_cont(token) > size_of_cont(xs)) | |
return false; | |
return get_segment(size_of_cont(xs) - size_of_cont(token), size_of_cont(xs), xs) == token; | |
} | |
// API search type: all_by : ((a -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a containers contains a token as a segment. | |
// all_by(is_even, [2, 4, 6]) == true | |
// Returns true for empty containers. | |
template <typename UnaryPredicate, typename Container> bool all_by(UnaryPredicate p, const Container &xs) | |
{ | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
return std::all_of(std::begin(xs), std::end(xs), p); | |
} | |
// API search type: all : [Bool] -> Bool | |
// fwd bind count: 0 | |
// Checks if all values in a container evaluate to true. | |
// all([true, false, true]) == false | |
// Returns true for empty containers. | |
template <typename Container> bool all(const Container &xs) | |
{ | |
typedef typename Container::value_type T; | |
return all_by(identity<T>, xs); | |
} | |
// API search type: all_the_same_by : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all values in a container are equal using a binary predicate. | |
// Returns true for empty containers. | |
template <typename Container, typename BinaryPredicate> bool all_the_same_by(BinaryPredicate p, const Container &xs) | |
{ | |
internal::check_binary_predicate_for_container<BinaryPredicate, Container>(); | |
if (size_of_cont(xs) < 2) | |
return true; | |
auto unaryPredicate = bind_1st_of_2(p, xs.front()); | |
return all_by(unaryPredicate, xs); | |
} | |
// API search type: all_the_same_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all values in a container are equal using a transformer. | |
// Returns true for empty containers. | |
template <typename Container, typename F> bool all_the_same_on(F f, const Container &xs) | |
{ | |
if (size_of_cont(xs) < 2) | |
return true; | |
auto unaryPredicate = is_equal_by_to(f, f(xs.front())); | |
return all_by(unaryPredicate, xs); | |
} | |
// API search type: all_the_same : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if all values in a container are equal. | |
// Returns true for empty containers. | |
template <typename Container> bool all_the_same(const Container &xs) | |
{ | |
typedef typename Container::value_type T; | |
auto binaryPredicate = std::equal_to<T>(); | |
return all_the_same_by(binaryPredicate, xs); | |
} | |
// API search type: numbers_step : (a, a, a) -> [a] | |
// fwd bind count: 2 | |
// Return a sequence of numbers using a specific step. | |
// numbers_step(2, 9, 2) == [2, 4, 6, 8] | |
template <typename T, typename ContainerOut = std::vector<T>> | |
ContainerOut numbers_step(const T start, const T end, const T step) | |
{ | |
ContainerOut result; | |
if ((step > 0 && start >= end) || (step < 0 && start <= end) || step == 0) | |
{ | |
return result; | |
} | |
std::size_t size = static_cast<std::size_t>((end - start) / step); | |
internal::prepare_container(result, size); | |
auto it = internal::get_back_inserter<ContainerOut>(result); | |
for (T x = start; x < end; x += step) | |
*it = x; | |
return result; | |
} | |
// API search type: numbers : (a, a) -> [a] | |
// fwd bind count: 1 | |
// Return an ascending sequence of numbers.. | |
// Also known as range. | |
// numbers(2, 9) == [2, 3, 4, 5, 6, 7, 8] | |
template <typename T, typename ContainerOut = std::vector<T>> ContainerOut numbers(const T start, const T end) | |
{ | |
return numbers_step<T, ContainerOut>(start, end, 1); | |
} | |
// API search type: singleton_seq : a -> [a] | |
// fwd bind count: 0 | |
// Construct a sequence containing a single value. | |
// singleton_seq(3) == [3]. | |
template <typename T, typename ContainerOut = std::vector<T>> ContainerOut singleton_seq(const T &x) | |
{ | |
return ContainerOut(1, x); | |
} | |
// API search type: all_idxs : [a] -> [Int] | |
// fwd bind count: 0 | |
// Returns a vector containing all valid indices of sequence xs. | |
// all_idxs([6,4,7,6]) == [0,1,2,3] | |
template <typename Container> std::vector<std::size_t> all_idxs(const Container &xs) | |
{ | |
return numbers<std::size_t>(0, size_of_cont(xs)); | |
} | |
// API search type: init : [a] -> [a] | |
// fwd bind count: 0 | |
// init([0,1,2,3]) == [0,1,2] | |
// Unsafe! xs must be non-empty. | |
template <typename Container, typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut init(Container &&xs) | |
{ | |
assert(!is_empty(xs)); | |
return get_segment(0, size_of_cont(std::forward<Container>(xs)) - 1, xs); | |
} | |
// API search type: tail : [a] -> [a] | |
// fwd bind count: 0 | |
// Drops the first element of a container, keeps the rest. Unsafe! | |
// tail([0,1,2,3]) == [1,2,3] | |
// Unsafe! xs must be non-empty. | |
template <typename Container, typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut tail(Container &&xs) | |
{ | |
assert(!is_empty(xs)); | |
return get_segment(1, size_of_cont(std::forward<Container>(xs)), xs); | |
} | |
// API search type: head : [a] -> a | |
// fwd bind count: 0 | |
// Return the first element of a container. | |
// head([0,1,2,3]) == 0 | |
// Unsafe! xs must be non-empty. | |
template <typename Container> typename Container::value_type head(const Container &xs) | |
{ | |
assert(!is_empty(xs)); | |
return xs.front(); | |
} | |
// API search type: last : [a] -> a | |
// fwd bind count: 0 | |
// Return the last element of a container. | |
// last([0,1,2,3]) == 3 | |
// Unsafe! xs must be non-empty. | |
template <typename Container> typename Container::value_type last(const Container &xs) | |
{ | |
assert(!is_empty(xs)); | |
return xs.back(); | |
} | |
// API search type: mean_stddev : [a] -> (a, a) | |
// fwd bind count: 0 | |
// Calculates the mean and the population standard deviation. | |
// mean_stddev([4, 8]) == (6, 2) | |
// mean_stddev([1, 3, 7, 4]) == (3.75, 2.5) | |
// xs must be non-empty. | |
template <typename Result, typename Container> std::pair<Result, Result> mean_stddev(const Container &xs) | |
{ | |
assert(size_of_cont(xs) != 0); | |
// http://stackoverflow.com/a/7616783/1866775 | |
Result sum = | |
static_cast<Result>(internal::accumulate(xs.begin(), xs.end(), static_cast<typename Container::value_type>(0))); | |
Result mean = sum / static_cast<Result>(xs.size()); | |
std::vector<Result> diff(xs.size()); | |
std::transform(xs.begin(), xs.end(), diff.begin(), [mean](Result x) { return x - mean; }); | |
Result sq_sum = std::inner_product(diff.begin(), diff.end(), diff.begin(), static_cast<Result>(0)); | |
Result stddev = std::sqrt(sq_sum / static_cast<Result>(xs.size())); | |
return std::make_pair(mean, stddev); | |
} | |
// API search type: count_occurrences_by : ((a -> b), [a]) -> Map b Int | |
// fwd bind count: 1 | |
// Returns a discrete frequency distribution of the elements in a container | |
// applying a specific transformer. | |
// count_occurrences_by(floor, [1.1, 2.3, 2.7, 3.6, 2.4]) == [(1, 1), (2, 3), (3, 1)] | |
// O(n) | |
template <typename F, typename ContainerIn> auto count_occurrences_by(F f, const ContainerIn &xs) | |
{ | |
using In = typename ContainerIn::value_type; | |
using MapOut = std::map<std::decay_t<internal::invoke_result_t<F, In>>, std::size_t>; | |
internal::trigger_static_asserts<internal::unary_function_tag, F, typename ContainerIn::value_type>(); | |
MapOut result; | |
for (const auto &x : xs) | |
{ | |
++result[internal::invoke(f, x)]; | |
} | |
return result; | |
} | |
// API search type: count_occurrences : [a] -> Map a Int | |
// fwd bind count: 0 | |
// Returns a discrete frequency distribution of the elements in a container | |
// applying a specific transformer. | |
// Can be used to create a histogram. | |
// count_occurrences([1,2,2,3,2]) == [(1, 1), (2, 3), (3, 1)] | |
// O(n) | |
template <typename ContainerIn, typename MapOut = typename std::map<typename ContainerIn::value_type, std::size_t>> | |
MapOut count_occurrences(const ContainerIn &xs) | |
{ | |
return count_occurrences_by(identity<typename ContainerIn::value_type>, xs); | |
} | |
// API search type: lexicographical_less_by : (((a, a) -> Bool), [a], [a]) -> Bool | |
// fwd bind count: 2 | |
// Lexicographical less-than comparison using a specific predicate. | |
// lexicographical_less_by((<), [0,1,2,2,4,5], [0,1,2,3,4,5]) == true | |
// lexicographical_less_by((<), "012245", "012345") == true | |
// lexicographical_less_by((<), "01234", "012345") == true | |
// lexicographical_less_by((<), "012345", "01234") == false | |
// lexicographical_less_by((<), "012345", "012345") == false | |
template <typename Container, typename BinaryPredicate> | |
bool lexicographical_less_by(BinaryPredicate p, const Container &xs, const Container &ys) | |
{ | |
internal::check_binary_predicate_for_container<BinaryPredicate, Container>(); | |
auto itXs = std::begin(xs); | |
auto itYs = std::begin(ys); | |
while (itXs != std::end(xs) && itYs != std::end(ys)) | |
{ | |
if (internal::invoke(p, *itXs, *itYs)) | |
{ | |
return true; | |
} | |
if (internal::invoke(p, *itYs, *itXs)) | |
{ | |
return false; | |
} | |
++itXs; | |
++itYs; | |
} | |
if (size_of_cont(xs) < size_of_cont(ys)) | |
{ | |
return true; | |
} | |
return false; | |
} | |
// API search type: lexicographical_less : ([a], [a]) -> Bool | |
// fwd bind count: 1 | |
// Lexicographical less-than comparison. | |
// lexicographical_less([0,1,2,2,4,5], [0,1,2,3,4,5]) == true | |
// lexicographical_less("012245", "012345") == true | |
// lexicographical_less("01234", "012345") == true | |
// lexicographical_less("012345", "01234") == false | |
// lexicographical_less("012345", "012345") == false | |
template <typename Container> bool lexicographical_less(const Container &xs, const Container &ys) | |
{ | |
return lexicographical_less_by(is_less<typename Container::value_type>, xs, ys); | |
} | |
// API search type: lexicographical_sort : [[a]] -> [[a]] | |
// fwd bind count: 0 | |
// sort by lexicographical_less | |
template <typename Container, typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut lexicographical_sort(Container &&xs) | |
{ | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return sort_by(lexicographical_less<T>, std::forward<Container>(xs)); | |
} | |
// API search type: replicate : (Int, a) -> [a] | |
// fwd bind count: 1 | |
// Create a sequence containing x n times. | |
// replicate(3, 1) == [1, 1, 1] | |
template <typename T, typename ContainerOut = std::vector<T>> ContainerOut replicate(std::size_t n, const T &x) | |
{ | |
return ContainerOut(n, x); | |
} | |
namespace internal | |
{ | |
template <typename UnaryPredicate, typename T> | |
T instead_of_if(internal::reuse_container_t, UnaryPredicate pred, const T &alt, T &&x) | |
{ | |
if (internal::invoke(pred, x)) | |
return alt; | |
else | |
return std::forward<T>(x); | |
} | |
template <typename UnaryPredicate, typename T> | |
T instead_of_if(internal::create_new_container_t, UnaryPredicate pred, const T &alt, const T &x) | |
{ | |
if (internal::invoke(pred, x)) | |
return alt; | |
else | |
return x; | |
} | |
} // namespace internal | |
// API search type: instead_of_if : ((a -> Bool), a, a) -> a | |
// fwd bind count: 2 | |
// Return alt if pred(x), otherwise x itself. | |
template <typename UnaryPredicate, typename T, typename TAlt> | |
auto instead_of_if(UnaryPredicate pred, const TAlt &alt, T &&x) | |
{ | |
return internal::instead_of_if(internal::can_reuse_v<T>{}, pred, alt, std::forward<T>(x)); | |
} | |
// API search type: instead_of_if_empty : ((a -> Bool), [a], [a]) -> [a] | |
// fwd bind count: 2 | |
// Return alt if xs is empty, otherwise xs itself. | |
template <typename Container, typename ContainerAlt, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut instead_of_if_empty(const ContainerAlt &alt, Container &&xs) | |
{ | |
return instead_of_if(is_empty<internal::remove_const_and_ref_t<Container>>, alt, std::forward<Container>(xs)); | |
} | |
} // namespace fplus |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. | |
// https://github.com/Dobiasd/FunctionalPlus | |
// Distributed under the Boost Software License, Version 1.0. | |
// (See accompanying file LICENSE_1_0.txt or copy at | |
// http://www.boost.org/LICENSE_1_0.txt) | |
#pragma once | |
#include <fplus/compare.hpp> | |
#include <fplus/composition.hpp> | |
#include <fplus/container_traits.hpp> | |
#include <fplus/maybe.hpp> | |
#include <fplus/internal/container_common.hpp> | |
#include <fplus/internal/invoke.hpp> | |
#include <fplus/internal/meta.hpp> | |
#include <algorithm> | |
#include <cassert> | |
#include <cmath> | |
#include <cstddef> | |
#include <iterator> | |
#include <numeric> | |
namespace fplus { | |
namespace internal { | |
template<typename UnaryPredicate, typename Container> | |
void | |
check_unary_predicate_for_container() | |
{ | |
internal::check_unary_predicate_for_type<UnaryPredicate, | |
typename Container::value_type>(); | |
} | |
template<typename F, typename Container> | |
void | |
check_index_with_type_predicate_for_container() | |
{ | |
typedef typename Container::value_type T; | |
internal:: | |
trigger_static_asserts<internal::binary_function_tag, F, std::size_t, T>(); | |
static_assert( | |
std::is_convertible<internal::invoke_result_t<F, std::size_t, T>, | |
bool>::value, | |
"Function must return bool."); | |
} | |
template<typename Compare, typename Container> | |
void | |
check_compare_for_container() | |
{ | |
typedef typename Container::value_type T; | |
internal:: | |
trigger_static_asserts<internal::binary_predicate_tag, Compare, T, T>(); | |
} | |
template<typename BinaryPredicate, typename Container> | |
void | |
check_binary_predicate_for_container() | |
{ | |
typedef typename Container::value_type T; | |
internal::trigger_static_asserts<internal::binary_predicate_tag, | |
BinaryPredicate, | |
T, | |
T>(); | |
} | |
// PrepareContainer and BackInserter are overloaded | |
// to increase performance on std::vector and std::string | |
// by using std::vector<T>::reserve | |
// and std::back_inserter instead of std::back_inserter. | |
// In VC2015, release mode, Celsius W520 Xeon | |
// this leads to an increase in performance of about a factor of 3 | |
// for transform. | |
template<typename C> | |
void | |
prepare_container( | |
const std::basic_string<C, std::char_traits<C>, std::allocator<C>>& ys, | |
std::size_t size) | |
{ | |
ys.reserve(size); | |
} | |
template<typename Y> | |
void | |
prepare_container(std::vector<Y>& ys, std::size_t size) | |
{ | |
ys.reserve(size); | |
} | |
template<typename T, std::size_t N> | |
void | |
prepare_container(std::array<T, N>&, std::size_t size) | |
{ | |
assert(size == N); | |
unused(size); | |
} | |
template<typename Y> | |
void | |
prepare_container(std::unordered_set<Y>& ys, std::size_t size) | |
{ | |
ys.reserve(size); | |
} | |
template<typename Key, typename T> | |
void | |
prepare_container(std::unordered_map<Key, T>& ys, std::size_t size) | |
{ | |
ys.reserve(size); | |
} | |
template<typename Y> | |
void | |
prepare_container(std::unordered_multiset<Y>& ys, std::size_t size) | |
{ | |
ys.reserve(size); | |
} | |
template<typename Key, typename T> | |
void | |
prepare_container(std::unordered_multimap<Key, T>& ys, std::size_t size) | |
{ | |
ys.reserve(size); | |
} | |
template<typename Container> | |
void | |
prepare_container(Container&, std::size_t) | |
{} | |
template<typename Container> | |
std::back_insert_iterator<Container> | |
get_back_inserter(std::string& ys) | |
{ | |
return std::back_inserter(ys); | |
} | |
template<typename Container, typename Y> | |
std::back_insert_iterator<Container> | |
get_back_inserter(std::vector<Y>& ys) | |
{ | |
return std::back_inserter(ys); | |
} | |
template<typename Container, typename Y> | |
std::back_insert_iterator<Container> | |
get_back_inserter(std::list<Y>& ys) | |
{ | |
return std::back_inserter(ys); | |
} | |
template<typename Container, typename Y> | |
std::back_insert_iterator<Container> | |
get_back_inserter(std::deque<Y>& ys) | |
{ | |
return std::back_inserter(ys); | |
} | |
// Avoid self-assignment. | |
template<typename T> | |
void | |
assign(T& x, T&& y) | |
{ | |
if (&x != &y) | |
x = std::move(y); | |
} | |
template<typename T, std::size_t N> | |
struct array_back_insert_iterator | |
: public std::back_insert_iterator<std::array<T, N>> | |
{ | |
typedef std::back_insert_iterator<std::array<T, N>> base_type; | |
explicit array_back_insert_iterator(std::array<T, N>& arr) | |
: base_type(arr) | |
, arr_ptr_(&arr) | |
, pos_(0) | |
{} | |
array_back_insert_iterator(const array_back_insert_iterator<T, N>& other) | |
: base_type(*other.arr_ptr_) | |
, arr_ptr_(other.arr_ptr_) | |
, pos_(other.pos_) | |
{} | |
array_back_insert_iterator<T, N>& operator=( | |
const array_back_insert_iterator<T, N>& other) | |
{ | |
arr_ptr_ = other.arr_ptr_; | |
pos_ = other.pos_; | |
return *this; | |
} | |
~array_back_insert_iterator() { assert(pos_ == 0 || pos_ == N); } | |
array_back_insert_iterator<T, N>& operator=(const T& x) | |
{ | |
assert(pos_ < N); | |
(*arr_ptr_)[pos_] = x; | |
++pos_; | |
return *this; | |
} | |
array_back_insert_iterator<T, N>& operator=(T&& x) | |
{ | |
assert(pos_ < N); | |
assign((*arr_ptr_)[pos_], std::move(x)); | |
++pos_; | |
return *this; | |
} | |
array_back_insert_iterator<T, N>& operator*() { return *this; } | |
array_back_insert_iterator<T, N>& operator++() { return *this; } | |
array_back_insert_iterator<T, N> operator++(int) { return *this; } | |
private: | |
std::array<T, N>* arr_ptr_; | |
std::size_t pos_; | |
}; | |
#if defined(_MSC_VER) && _MSC_VER >= 1900 | |
template<typename T, std::size_t N> | |
struct std::_Is_checked_helper<array_back_insert_iterator<T, N>> | |
: public true_type | |
{ // mark array_back_insert_iterator as checked | |
}; | |
#endif | |
template<typename Container, typename Y, std::size_t N> | |
array_back_insert_iterator<Y, N> | |
get_back_inserter(std::array<Y, N>& ys) | |
{ | |
return array_back_insert_iterator<Y, N>(ys); | |
} | |
template<typename Container> | |
std::insert_iterator<Container> | |
get_back_inserter(Container& ys) | |
{ | |
return std::inserter(ys, std::end(ys)); | |
} | |
template<typename Iterator> | |
void | |
advance_iterator(Iterator& it, std::size_t distance) | |
{ | |
std::advance(it, static_cast<typename Iterator::difference_type>(distance)); | |
} | |
template<typename T> | |
void | |
advance_iterator(T*& it, std::size_t distance) | |
{ | |
it += static_cast<std::ptrdiff_t>(distance); | |
} | |
template<typename Iterator> | |
Iterator | |
add_to_iterator(Iterator it, std::size_t distance = 1) | |
{ | |
return std::next(it, | |
static_cast<typename Iterator::difference_type>(distance)); | |
} | |
// GCC 4.9 does not support std::rbegin, std::rend and | |
// std::make_reverse_iterator | |
template<typename Iterator> | |
std::reverse_iterator<Iterator> | |
make_reverse_iterator(Iterator it) | |
{ | |
return std::reverse_iterator<Iterator>(it); | |
} | |
} // namespace internal | |
// API search type: is_even : Int -> Bool | |
// fwd bind count: 0 | |
// Checks if x is even. | |
template<typename X> | |
bool | |
is_even(X x) | |
{ | |
static_assert(std::is_integral<X>::value, "type must be integral"); | |
return x % 2 == 0; | |
} | |
// API search type: is_odd : Int -> Bool | |
// fwd bind count: 0 | |
// Checks if x is odd. | |
template<typename X> | |
bool | |
is_odd(X x) | |
{ | |
static_assert(std::is_integral<X>::value, "type must be integral"); | |
return x % 2 != 0; | |
} | |
// API search type: is_empty : [a] -> Bool | |
// fwd bind count: 0 | |
// Returns true if the container holds no elements. | |
// is_empty([1, 2]) == false | |
// is_empty([]) == true | |
template<typename Container> | |
bool | |
is_empty(const Container& xs) | |
{ | |
return xs.empty(); | |
} | |
// API search type: is_not_empty : [a] -> Bool | |
// fwd bind count: 0 | |
// Returns true if the container holds at least one element. | |
// is_not_empty([1, 2]) == true | |
template<typename Container> | |
bool | |
is_not_empty(const Container& xs) | |
{ | |
return !is_empty(xs); | |
} | |
// API search type: size_of_cont : [a] -> Int | |
// fwd bind count: 0 | |
// Returns the number of elements in the given container. | |
// size_of_cont([3, 4]) == 2 | |
template<typename Container> | |
std::size_t | |
size_of_cont(const Container& xs) | |
{ | |
return xs.size(); | |
} | |
// API search type: convert : a -> b | |
// fwd bind count: 0 | |
// Converts one type of element into another. | |
template<typename Dest, typename Source> | |
Dest | |
convert(const Source& x) | |
{ | |
return Dest(x); | |
} | |
// API search type: convert_elems : [a] -> [b] | |
// fwd bind count: 0 | |
// Converts all elements in a sequence to a different type. | |
// convert_elems<NewT>([1, 2, 3]) == [NewT(1), NewT(2), NewT(3)] | |
template<typename NewT, | |
typename ContainerIn, | |
typename ContainerOut = | |
typename internal::same_cont_new_t<ContainerIn, NewT, 0>::type> | |
ContainerOut | |
convert_elems(const ContainerIn& xs) | |
{ | |
static_assert( | |
std::is_constructible<NewT, typename ContainerIn::value_type>::value, | |
"Elements not convertible."); | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it = internal::get_back_inserter<ContainerOut>(ys); | |
// using 'for (const auto& x ...)' is even for ints as fast as | |
// using 'for (int x ...)' (GCC, O3), so there is no need to | |
// check if the type is fundamental and then dispatch accordingly. | |
for (const auto& x : xs) { | |
*it = convert<NewT>(x); | |
} | |
return ys; | |
} | |
// API search type: convert_container : [a] -> [a] | |
// fwd bind count: 0 | |
// Change the type of the container | |
// while keeping the elements in the sequence the same. | |
// convert_container([1, 2, 3]) == [1, 2, 3] | |
// Useful for example if you want to convert an std::list to an std::vector. | |
template<typename ContainerOut, typename ContainerIn> | |
ContainerOut | |
convert_container(const ContainerIn& xs) | |
{ | |
typedef typename ContainerIn::value_type SourceElem; | |
typedef typename ContainerOut::value_type DestElem; | |
static_assert(std::is_same<DestElem, SourceElem>::value, | |
"Source and dest container must have the same value_type"); | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto itOut = internal::get_back_inserter<ContainerOut>(ys); | |
std::copy(std::begin(xs), std::end(xs), itOut); | |
return ys; | |
} | |
// API search type: convert_container_and_elems : [a] -> [b] | |
// fwd bind count: 0 | |
// Converts between different containers and elements. | |
// Dest elements are allowed to have explicit constructors. | |
// convert([1, 2, 3]) == [1, 2, 3] | |
template<typename ContainerOut, typename ContainerIn> | |
ContainerOut | |
convert_container_and_elems(const ContainerIn& xs) | |
{ | |
static_assert(std::is_convertible<typename ContainerIn::value_type, | |
typename ContainerOut::value_type>::value, | |
"Elements not convertible."); | |
typedef typename ContainerOut::value_type DestElem; | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it = internal::get_back_inserter<ContainerOut>(ys); | |
for (const auto& x : xs) { | |
*it = convert<DestElem>(x); | |
} | |
return ys; | |
} | |
namespace internal { | |
template<typename Container> | |
Container | |
get_segment(internal::reuse_container_t, | |
std::size_t idx_begin, | |
std::size_t idx_end, | |
Container&& xs) | |
{ | |
idx_end = std::min(idx_end, size_of_cont(xs)); | |
if (idx_end <= idx_begin) { | |
xs.clear(); | |
return std::forward<Container>(xs); | |
} | |
auto itBegin = std::begin(xs); | |
internal::advance_iterator(itBegin, idx_begin); | |
auto itEnd = itBegin; | |
internal::advance_iterator(itEnd, idx_end - idx_begin); | |
xs.erase(std::copy(itBegin, itEnd, std::begin(xs)), std::end(xs)); | |
return std::forward<Container>(xs); | |
} | |
template<typename Container> | |
Container | |
get_segment(internal::create_new_container_t, | |
std::size_t idx_begin, | |
std::size_t idx_end, | |
const Container& xs) | |
{ | |
idx_end = std::min(idx_end, size_of_cont(xs)); | |
if (idx_end <= idx_begin) { | |
return {}; | |
} | |
Container result; | |
auto itBegin = std::begin(xs); | |
internal::advance_iterator(itBegin, idx_begin); | |
auto itEnd = itBegin; | |
internal::advance_iterator(itEnd, idx_end - idx_begin); | |
std::copy(itBegin, itEnd, internal::get_back_inserter(result)); | |
return result; | |
} | |
} // namespace internal | |
// API search type: get_segment : (Int, Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Return a defined segment from the sequence. | |
// get_segment(2, 5, [0,1,2,3,4,5,6,7,8]) == [2,3,4] | |
// get_segment(2, 15, [0,1,2,3,4,5,6,7,8]) == [2,3,4,5,6,7,8] | |
// get_segment(5, 2, [0,1,2,3,4,5,6,7,8]) == [] | |
// Also known as slice. | |
template<typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut | |
get_segment(std::size_t idx_begin, std::size_t idx_end, Container&& xs) | |
{ | |
return internal::get_segment(internal::can_reuse_v<Container>{}, | |
idx_begin, | |
idx_end, | |
std::forward<Container>(xs)); | |
} | |
namespace internal { | |
template<typename ContainerToken, typename Container> | |
Container | |
set_segment(internal::reuse_container_t, | |
std::size_t idx_begin, | |
const ContainerToken& token, | |
Container&& xs) | |
{ | |
assert(idx_begin + size_of_cont(token) < size_of_cont(xs)); | |
auto itBegin = std::begin(xs); | |
internal::advance_iterator(itBegin, idx_begin); | |
std::copy(std::begin(token), std::end(token), itBegin); | |
return std::forward<Container>(xs); | |
} | |
template<typename ContainerToken, typename Container> | |
Container | |
set_segment(internal::create_new_container_t, | |
std::size_t idx_begin, | |
const ContainerToken& token, | |
const Container& xs) | |
{ | |
Container result = xs; | |
return set_segment( | |
internal::reuse_container_t(), idx_begin, token, std::move(result)); | |
} | |
} // namespace internal | |
// API search type: set_segment : (Int, [a], [a]) -> [a] | |
// fwd bind count: 2 | |
// Replace part of a sequence with a token. | |
// set_segment(2, [9,9,9], [0,1,2,3,4,5,6,7,8]) == [0,1,9,9,9,5,6,7,8] | |
// Crashes on invalid indices. | |
// Also known as replace_segment. | |
template<typename ContainerToken, | |
typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut | |
set_segment(std::size_t idx_begin, const ContainerToken& token, Container&& xs) | |
{ | |
return internal::set_segment(internal::can_reuse_v<Container>{}, | |
idx_begin, | |
token, | |
std::forward<Container>(xs)); | |
} | |
namespace internal { | |
template<typename Container> | |
Container | |
remove_segment(internal::reuse_container_t, | |
std::size_t idx_begin, | |
std::size_t idx_end, | |
Container&& xs) | |
{ | |
assert(idx_begin <= idx_end); | |
assert(idx_end <= size_of_cont(xs)); | |
auto firstBreakIt = std::begin(xs); | |
internal::advance_iterator(firstBreakIt, idx_begin); | |
auto secondBreakIt = std::begin(xs); | |
internal::advance_iterator(secondBreakIt, idx_end); | |
xs.erase(std::copy(secondBreakIt, std::end(xs), firstBreakIt), std::end(xs)); | |
return std::forward<Container>(xs); | |
} | |
template<typename Container> | |
Container | |
remove_segment(internal::create_new_container_t, | |
std::size_t idx_begin, | |
std::size_t idx_end, | |
const Container& xs) | |
{ | |
assert(idx_begin <= idx_end); | |
assert(idx_end <= size_of_cont(xs)); | |
Container result; | |
std::size_t length = idx_end - idx_begin; | |
internal::prepare_container(result, size_of_cont(xs) - length); | |
auto firstBreakIt = std::begin(xs); | |
internal::advance_iterator(firstBreakIt, idx_begin); | |
std::copy(std::begin(xs), firstBreakIt, internal::get_back_inserter(result)); | |
auto secondBreakIt = std::begin(xs); | |
internal::advance_iterator(secondBreakIt, idx_end); | |
std::copy(secondBreakIt, std::end(xs), internal::get_back_inserter(result)); | |
return result; | |
} | |
} // namespace internal | |
// API search type: remove_segment : (Int, Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Cuts our a defined segment from the sequence. | |
// remove_segment(2, 5, [0,1,2,3,4,5,6,7]) == [0,1,5,6,7] | |
// crashes on invalid indices | |
template<typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut | |
remove_segment(std::size_t idx_begin, std::size_t idx_end, Container&& xs) | |
{ | |
return internal::remove_segment(internal::can_reuse_v<Container>{}, | |
idx_begin, | |
idx_end, | |
std::forward<Container>(xs)); | |
} | |
// API search type: insert_at : (Int, [a], [a]) -> [a] | |
// fwd bind count: 2 | |
// Inserts a token into a sequence at a specific position. | |
// insert_at(2, [8,9], [0,1,2,3,4]) == [0,1,8,9,2,3,4] | |
// Unsafe! Crashes on invalid index. | |
template<typename Container> | |
Container | |
insert_at(std::size_t idx_begin, const Container& token, const Container& xs) | |
{ | |
assert(idx_begin <= size_of_cont(xs)); | |
Container result; | |
internal::prepare_container(result, size_of_cont(xs) + size_of_cont(token)); | |
auto breakIt = std::begin(xs); | |
internal::advance_iterator(breakIt, idx_begin); | |
std::copy(std::begin(xs), breakIt, internal::get_back_inserter(result)); | |
std::copy( | |
std::begin(token), std::end(token), internal::get_back_inserter(result)); | |
std::copy(breakIt, std::end(xs), internal::get_back_inserter(result)); | |
return result; | |
} | |
// API search type: elem_at_idx : (Int, [a]) -> a | |
// fwd bind count: 1 | |
// Return the nth element of a sequence. | |
// elem_at_idx(2, [7,6,5,4,3]) == 5 | |
// Unsafe! Crashes on invalid index. | |
template<typename Container> | |
auto | |
elem_at_idx(std::size_t idx, const Container& xs) | |
{ | |
assert(idx < size_of_cont(xs)); | |
auto it = std::begin(xs); | |
internal::advance_iterator(it, idx); | |
return *it; | |
} | |
// API search type: elem_at_idx_maybe : (Int, [a]) -> Maybe a | |
// fwd bind count: 1 | |
// Return the nth element of a sequence if existing. | |
// elem_at_idx_maybe(2, [7,6,5,4,3]) == Just 5 | |
// elem_at_idx_maybe(9, [7,6,5,4,3]) == Nothing | |
// Use elem_at_idx_or_nothing if you want to provide a signed index type. | |
template<typename Container, typename T = typename Container::value_type> | |
maybe<T> | |
elem_at_idx_maybe(std::size_t idx, const Container& xs) | |
{ | |
if (size_of_cont(xs) < idx) { | |
return {}; | |
} | |
auto it = std::begin(xs); | |
internal::advance_iterator(it, idx); | |
return *it; | |
} | |
// API search type: elems_at_idxs : ([Int], [a]) -> [a] | |
// fwd bind count: 1 | |
// Construct a subsequence from the elements with the given indices. | |
// elem_at_idxs([1, 3], [7,6,5,4,3]) == [6, 4] | |
template<typename Container, | |
typename ContainerIdxs, | |
typename T = typename Container::value_type, | |
typename ContainerOut = std::vector<T>> | |
std::vector<T> | |
elems_at_idxs(const ContainerIdxs& idxs, const Container& xs) | |
{ | |
static_assert( | |
std::is_same<typename ContainerIdxs::value_type, std::size_t>::value, | |
"Indices must be std::size_t"); | |
ContainerOut result; | |
internal::prepare_container(result, size_of_cont(idxs)); | |
auto itOut = internal::get_back_inserter(result); | |
for (std::size_t idx : idxs) { | |
*itOut = elem_at_idx(idx, xs); | |
} | |
return result; | |
} | |
namespace internal { | |
template<typename Container, typename F> | |
Container | |
transform(internal::reuse_container_t, F f, Container&& xs) | |
{ | |
internal::trigger_static_asserts<internal::unary_function_tag, | |
F, | |
decltype(*std::begin(xs))>(); | |
std::transform(std::begin(xs), std::end(xs), std::begin(xs), f); | |
return std::forward<Container>(xs); | |
} | |
template<typename ContainerOut, typename F, typename ContainerIn> | |
ContainerOut | |
transform(internal::create_new_container_t, F f, const ContainerIn& xs) | |
{ | |
internal::trigger_static_asserts<internal::unary_function_tag, | |
F, | |
decltype(*std::begin(xs))>(); | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it = internal::get_back_inserter<ContainerOut>(ys); | |
std::transform(std::begin(xs), std::end(xs), it, f); | |
return ys; | |
} | |
} // namespace internal | |
// API search type: transform : ((a -> b), [a]) -> [b] | |
// fwd bind count: 1 | |
// Apply a function to every element in a sequence. | |
// transform((*2), [1, 3, 4]) == [2, 6, 8] | |
// Also known as map or fmap. | |
template< | |
typename F, | |
typename ContainerIn, | |
typename ContainerOut = typename internal::same_cont_new_t_from_unary_f< | |
internal::remove_const_and_ref_t<ContainerIn>, | |
F, | |
0>::type> | |
ContainerOut | |
transform(F f, ContainerIn&& xs) | |
{ | |
using reuse_t = typename std::conditional< | |
std::is_same<internal::can_reuse_v<ContainerIn>, | |
internal::reuse_container_t>::value && | |
std::is_base_of<std::true_type, | |
internal::has_order<ContainerIn>>::value && | |
std::is_same<internal::remove_const_and_ref_t<ContainerIn>, | |
ContainerOut>::value, | |
internal::reuse_container_t, | |
internal::create_new_container_t>::type; | |
return internal::transform<ContainerOut>( | |
reuse_t{}, f, std::forward<ContainerIn>(xs)); | |
} | |
// API search type: transform_convert : ((a -> b), [a]) -> [b] | |
// fwd bind count: 1 | |
// transform_convert((*2), [1, 3, 4]) == [2, 6, 8] | |
// Same as transform, but makes it easy to | |
// use an output container type different from the input container type. | |
template<typename ContainerOut, typename F, typename ContainerIn> | |
ContainerOut | |
transform_convert(F f, const ContainerIn& xs) | |
{ | |
internal::trigger_static_asserts<internal::unary_function_tag, | |
F, | |
typename ContainerIn::value_type>(); | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it = internal::get_back_inserter<ContainerOut>(ys); | |
std::transform(std::begin(xs), std::end(xs), it, f); | |
return ys; | |
} | |
// API search type: transform_inner : ((a -> b), [[a]]) -> [[b]] | |
// fwd bind count: 1 | |
// Applies a function to the elements of the inner containers | |
// of a nested sequence. | |
// transform_inner((*2), [[1, 3, 4], [1, 2]]) == [[2, 6, 8], [2, 4]] | |
// Also known as transform_nested, map_nested or map_inner. | |
template<typename F, | |
typename ContainerIn, | |
typename ContainerOut = typename internal::same_cont_new_t< | |
ContainerIn, | |
typename internal::same_cont_new_t_from_unary_f< | |
typename ContainerIn::value_type, | |
F, | |
0>::type, | |
0>::type> | |
ContainerOut | |
transform_inner(F f, const ContainerIn& xs) | |
{ | |
internal::trigger_static_asserts< | |
internal::unary_function_tag, | |
F, | |
typename ContainerIn::value_type::value_type>(); | |
return fplus::transform( | |
fplus::bind_1st_of_2( | |
fplus::transform<F, const typename ContainerIn::value_type&>, f), | |
xs); | |
} | |
namespace internal { | |
template<typename Container> | |
Container | |
reverse(internal::reuse_container_t, Container&& xs) | |
{ | |
static_assert(internal::has_order<Container>::value, | |
"Reverse: Container has no order."); | |
std::reverse(std::begin(xs), std::end(xs)); | |
return std::forward<Container>(xs); | |
} | |
template<typename Container> | |
Container | |
reverse(internal::create_new_container_t, const Container& xs) | |
{ | |
static_assert(internal::has_order<Container>::value, | |
"Reverse: Container has no order."); | |
Container ys = xs; | |
std::reverse(std::begin(ys), std::end(ys)); | |
return ys; | |
} | |
} // namespace internal | |
// API search type: reverse : [a] -> [a] | |
// fwd bind count: 0 | |
// Reverse a sequence. | |
// reverse([0,4,2,6]) == [6,2,4,0] | |
template<typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut | |
reverse(Container&& xs) | |
{ | |
return internal::reverse(internal::can_reuse_v<Container>{}, | |
std::forward<Container>(xs)); | |
} | |
// API search type: take : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Return the first n elements of a sequence xs. | |
// In case n >= length(xs), xs is returned. | |
// take(3, [0,1,2,3,4,5,6,7]) == [0,1,2] | |
// take(10, [0,1,2]) == [0,1,2] | |
template<typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut | |
take(std::size_t amount, Container&& xs) | |
{ | |
if (amount >= size_of_cont(xs)) | |
return xs; | |
return get_segment(0, amount, std::forward<Container>(xs)); | |
} | |
// API search type: take_exact : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Return exactly the first n elements of a sequence xs. | |
// Unsafe! Crashes then sequence is too short. | |
// take_exact(3, [0,1,2,3,4,5,6,7]) == [0,1,2] | |
// take_exact(10, [0,1,2]) == crash | |
template<typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut | |
take_exact(std::size_t amount, Container&& xs) | |
{ | |
return get_segment(0, amount, std::forward<Container>(xs)); | |
} | |
// API search type: take_cyclic : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Takes n elements from a sequence considering it as cyclic. | |
// take_cyclic(5, [0,1,2,3]) == [0,1,2,3,0] | |
// take_cyclic(7, [0,1,2,3]) == [0,1,2,3,0,1,2] | |
// take_cyclic(7, [0,1]) == [0,1,0,1,0,1,0] | |
// take_cyclic(2, [0,1,2,3]) == [0,1] | |
// take_cyclic(3, [0]) == [0,0,0] | |
// take_cyclic(3, []) == crash! | |
// Also known as take_wrap. | |
// xs must be non-empty. | |
template<typename Container> | |
Container | |
take_cyclic(std::size_t amount, const Container& xs) | |
{ | |
assert(!xs.empty()); | |
Container ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it_out = internal::get_back_inserter(ys); | |
auto it_in = std::begin(xs); | |
while (amount != 0) { | |
*it_out = *it_in; | |
--amount; | |
++it_in; | |
if (it_in == std::end(xs)) { | |
it_in = std::begin(xs); | |
} | |
} | |
return ys; | |
} | |
// API search type: drop : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Skip the first n elements of a sequence xs. | |
// If n > length(xs) an empty sequence is returned. | |
// drop(3, [0,1,2,3,4,5,6,7]) == [3,4,5,6,7] | |
// Also known as skip. | |
template<typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut | |
drop(std::size_t amount, Container&& xs) | |
{ | |
if (amount >= size_of_cont(xs)) | |
return ContainerOut(); | |
return get_segment(amount, size_of_cont(xs), std::forward<Container>(xs)); | |
} | |
// API search type: take_last : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Return the last n elements of a sequence xs. | |
// In case n >= length(xs), xs is returned. | |
// take_last(3, [0,1,2,3,4,5,6,7]) == [5,6,7] | |
// take_last(10, [0,1,2]) == [0,1,2] | |
template<typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut | |
take_last(std::size_t amount, Container&& xs) | |
{ | |
if (amount >= size_of_cont(xs)) | |
return xs; | |
return drop(size_of_cont(xs) - amount, std::forward<Container>(xs)); | |
} | |
// API search type: drop_last : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Skip the last n elements of a sequence xs. | |
// If n > length(xs) an empty sequence is returned. | |
// drop_last(3, [0,1,2,3,4,5,6,7]) == [0,1,2,3,4] | |
template<typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut | |
drop_last(std::size_t amount, Container&& xs) | |
{ | |
if (amount >= size_of_cont(xs)) | |
return ContainerOut(); | |
return take(size_of_cont(xs) - amount, std::forward<Container>(xs)); | |
} | |
// API search type: drop_exact : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Skip exactly the first n elements of a sequence xs. | |
// Unsafe! Crashes when xs is too short. | |
// drop_exact(3, [0,1,2,3,4,5,6,7]) == [3,4,5,6,7] | |
// drop_exact(10, [0,1,2,3,4,5,6,7]) == crash | |
template<typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut | |
drop_exact(std::size_t amount, Container&& xs) | |
{ | |
return get_segment(amount, size_of_cont(xs), std::forward<Container>(xs)); | |
} | |
// API search type: take_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Take elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// take_while(is_even, [0,2,4,5,6,7,8]) == [0,2,4] | |
template<typename Container, typename UnaryPredicate> | |
Container | |
take_while(UnaryPredicate pred, const Container& xs) | |
{ | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
auto itFirst = std::find_if(std::begin(xs), std::end(xs), logical_not(pred)); | |
if (itFirst == std::end(xs)) | |
return xs; | |
return Container(std::begin(xs), itFirst); | |
} | |
// API search type: take_last_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Take elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// take_last_while(is_even, [0,2,7,5,6,4,8]) == [6,4,8] | |
template<typename Container, typename UnaryPredicate> | |
Container | |
take_last_while(UnaryPredicate pred, const Container& xs) | |
{ | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
const auto r_begin = internal::make_reverse_iterator(std::end(xs)); | |
const auto r_end = internal::make_reverse_iterator(std::begin(xs)); | |
const auto itFirstReverse = std::find_if(r_begin, r_end, logical_not(pred)); | |
if (itFirstReverse == r_begin) | |
return Container(); | |
if (itFirstReverse == r_end) | |
return xs; | |
return Container(itFirstReverse.base(), std::end(xs)); | |
} | |
// API search type: drop_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Remove elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// drop_while(is_even, [0,2,4,5,6,7,8]) == [5,6,7,8] | |
// Also known as trim_left_by. | |
template<typename Container, typename UnaryPredicate> | |
Container | |
drop_while(UnaryPredicate pred, const Container& xs) | |
{ | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
auto itFirstNot = std::find_if_not(std::begin(xs), std::end(xs), pred); | |
if (itFirstNot == std::end(xs)) | |
return Container(); | |
return Container(itFirstNot, std::end(xs)); | |
} | |
// API search type: drop_last_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Remove elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// drop_last_while(is_even, [0,2,7,5,6,4,8]) == [0,2,7,5] | |
template<typename Container, typename UnaryPredicate> | |
Container | |
drop_last_while(UnaryPredicate pred, const Container& xs) | |
{ | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
const auto r_begin = internal::make_reverse_iterator(std::end(xs)); | |
const auto r_end = internal::make_reverse_iterator(std::begin(xs)); | |
const auto itFirstNotReverse = std::find_if_not(r_begin, r_end, pred); | |
if (itFirstNotReverse == r_begin) | |
return xs; | |
if (itFirstNotReverse == r_end) | |
return Container(); | |
return Container(std::begin(xs), itFirstNotReverse.base()); | |
} | |
// API search type: fold_left : (((a, b) -> a), a, [b]) -> a | |
// fwd bind count: 2 | |
// fold_left((+), 0, [1, 2, 3]) == ((0+1)+2)+3 == 6 | |
// Takes the second argument and the first item of the list | |
// and applies the function to them, | |
// then feeds the function with this result and the second argument and so on. | |
template<typename F, typename Container, typename Acc> | |
Acc | |
fold_left(F f, const Acc& init, const Container& xs) | |
{ | |
using std::begin; | |
using std::end; | |
return internal::accumulate(begin(xs), end(xs), init, f); | |
} | |
// API search type: reduce : (((a, a) -> a), a, [a]) -> a | |
// fwd bind count: 2 | |
// reduce((+), 0, [1, 2, 3]) == (0+1+2+3) == 6 | |
// Combines the initial value and all elements of the sequence | |
// using the given function. | |
// The set of f, init and value_type should form a monoid. | |
template<typename F, typename Container> | |
typename Container::value_type | |
reduce(F f, const typename Container::value_type& init, const Container& xs) | |
{ | |
return fold_left(f, init, xs); | |
} | |
// API search type: fold_left_1 : (((a, a) -> a), [a]) -> a | |
// fwd bind count: 1 | |
// fold_left_1((+), [1, 2, 3]) == (1+2)+3 == 6 | |
// Takes the first 2 items of the list and applies the function to them, | |
// then feeds the function with this result and the third argument and so on. | |
// xs must be non-empty. | |
template<typename F, typename Container> | |
auto | |
fold_left_1(F f, const Container& xs) | |
{ | |
assert(!xs.empty()); | |
using std::begin; | |
using std::end; | |
const auto it = begin(xs); | |
return internal::accumulate(std::next(it), end(xs), *it, f); | |
} | |
// API search type: reduce_1 : (((a, a) -> a), [a]) -> a | |
// fwd bind count: 1 | |
// reduce_1((+), [1, 2, 3]) == (1+2+3) == 6 | |
// Joins all elements of the sequence using the given function. | |
// The set of f and value_type should form a semigroup. | |
template<typename F, typename Container> | |
typename Container::value_type | |
reduce_1(F f, const Container& xs) | |
{ | |
assert(!xs.empty()); | |
return fold_left_1(f, xs); | |
} | |
// API search type: fold_right : (((a, b) -> b), b) -> [a] -> b | |
// fwd bind count: 2 | |
// fold_right((+), 0, [1, 2, 3]) == 1+(2+(3+0)) == 6 | |
// Takes the second argument and the last item of the list | |
// and applies the function, | |
// then it takes the penultimate item from the end and the result, and so on. | |
template<typename F, typename Container, typename Acc> | |
Acc | |
fold_right(F f, const Acc& init, const Container& xs) | |
{ | |
return internal::accumulate(xs.rbegin(), xs.rend(), init, flip(f)); | |
} | |
// API search type: fold_right_1 : (((a, a) -> a), [a]) -> a | |
// fwd bind count: 1 | |
// fold_right_1((+), [1, 2, 3]) == 1+(2+3)) == 6 | |
// Takes the last two items of the list and applies the function, | |
// then it takes the third item from the end and the result, and so on. | |
template<typename F, typename Container> | |
auto | |
fold_right_1(F f, const Container& xs) | |
{ | |
assert(!xs.empty()); | |
const auto it = xs.rbegin(); | |
return internal::accumulate(std::next(it), xs.rend(), *it, flip(f)); | |
} | |
// API search type: scan_left : (((a, b) -> a), a, [b]) -> [a] | |
// fwd bind count: 2 | |
// scan_left((+), 0, [1, 2, 3]) == [0, 1, 3, 6] | |
// Takes the second argument and the first item of the list | |
// and applies the function to them, | |
// then feeds the function with this result and the second argument and so on. | |
// It returns the list of intermediate and final results. | |
template<typename F, typename ContainerIn, typename Acc> | |
auto | |
scan_left(F f, const Acc& init, const ContainerIn& xs) | |
{ | |
using ContainerOut = | |
typename internal::same_cont_new_t<ContainerIn, Acc, 1>::type; | |
ContainerOut result; | |
internal::prepare_container(result, size_of_cont(xs) + 1); | |
using std::begin; | |
using std::end; | |
internal::scan_impl( | |
f, init, internal::get_back_inserter(result), begin(xs), end(xs)); | |
return result; | |
} | |
// API search type: scan_left_1 : (((a, a) -> a), [a]) -> [a] | |
// fwd bind count: 1 | |
// scan_left_1((+), [1, 2, 3]) == [1, 3, 6] | |
// Takes the first 2 items of the list and applies the function to them, | |
// then feeds the function with this result and the third argument and so on. | |
// It returns the list of intermediate and final results. | |
// xs must be non-empty. | |
template<typename F, typename ContainerIn> | |
auto | |
scan_left_1(F f, const ContainerIn& xs) | |
{ | |
assert(!xs.empty()); | |
using std::begin; | |
using std::end; | |
const auto beginIt = begin(xs); | |
using ContainerOut = | |
typename internal::same_cont_new_t<ContainerIn, | |
internal::uncvref_t<decltype(*beginIt)>, | |
0>::type; | |
ContainerOut result; | |
internal::prepare_container(result, size_of_cont(xs)); | |
internal::scan_impl(f, | |
*beginIt, | |
internal::get_back_inserter(result), | |
std::next(beginIt), | |
end(xs)); | |
return result; | |
} | |
// API search type: scan_right : (((a, b) -> b), b, [a]) -> [b] | |
// fwd bind count: 2 | |
// scan_right((+), 0, [1, 2, 3]) == [6, 5, 3, 0] | |
// Takes the second argument and the last item of the list | |
// and applies the function, | |
// then it takes the penultimate item from the end and the result, and so on. | |
// It returns the list of intermediate and final results. | |
template< | |
typename F, | |
typename ContainerIn, | |
typename Acc = typename utils::function_traits<F>::template arg<1>::type, | |
typename ContainerOut = | |
typename internal::same_cont_new_t<ContainerIn, Acc, 1>::type> | |
ContainerOut | |
scan_right(F f, const Acc& init, const ContainerIn& xs) | |
{ | |
return reverse(scan_left(flip(f), init, reverse(xs))); | |
} | |
// API search type: scan_right_1 : (((a, a) -> a), [a]) -> [a] | |
// fwd bind count: 1 | |
// scan_right_1((+), [1, 2, 3]) == [6, 5, 3] | |
// Takes the last two items of the list and applies the function, | |
// then it takes the third item from the end and the result, and so on. | |
// It returns the list of inntermediate and final results. | |
template<typename F, | |
typename ContainerIn, | |
typename Acc = typename ContainerIn::value_type, | |
typename ContainerOut = | |
typename internal::same_cont_new_t<ContainerIn, Acc, 0>::type> | |
ContainerOut | |
scan_right_1(F f, const ContainerIn& xs) | |
{ | |
return reverse(scan_left_1(flip(f), reverse(xs))); | |
} | |
// API search type: sum : [a] -> a | |
// fwd bind count: 0 | |
// Adds up all values in a sequence. | |
// sum([0,3,1]) == 4 | |
// sum([]) == 0 | |
template<typename Container, typename T = typename Container::value_type> | |
T | |
sum(const Container& xs) | |
{ | |
T result = T(); | |
for (const auto& x : xs) { | |
result = result + x; | |
} | |
return result; | |
} | |
// API search type: product : [a] -> a | |
// fwd bind count: 0 | |
// Returns the product of all values in a sequence. | |
// product([3,1,2]) == 6 | |
// product([]) == 1 | |
template<typename Container, typename T = typename Container::value_type> | |
T | |
product(const Container& xs) | |
{ | |
T result{ 1 }; | |
for (const auto& x : xs) { | |
result = result * x; | |
} | |
return result; | |
} | |
namespace internal { | |
template<typename T, typename Container> | |
Container | |
append_elem(internal::reuse_container_t, const T& y, Container&& xs) | |
{ | |
*internal::get_back_inserter(xs) = y; | |
return std::forward<Container>(xs); | |
} | |
template<typename T, typename Container> | |
Container | |
append_elem(internal::create_new_container_t, const T& y, const Container& xs) | |
{ | |
Container result; | |
internal::prepare_container(result, size_of_cont(xs) + 1); | |
std::copy(std::begin(xs), std::end(xs), internal::get_back_inserter(result)); | |
*internal::get_back_inserter(result) = y; | |
return result; | |
} | |
} // namespace internal | |
// API search type: append_elem : (a, [a]) -> [a] | |
// fwd bind count: 1 | |
// Extends a sequence with one element at the back. | |
// append_elem([1, 2], 3) == [1, 2, 3] | |
template<typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>, | |
typename T = typename ContainerOut::value_type> | |
ContainerOut | |
append_elem(const T& y, Container&& xs) | |
{ | |
return internal::append_elem( | |
internal::can_reuse_v<Container>{}, y, std::forward<Container>(xs)); | |
} | |
namespace internal { | |
template<typename T> | |
std::list<T> | |
prepend_elem(internal::reuse_container_t, const T& y, std::list<T>&& xs) | |
{ | |
xs.push_front(y); | |
return std::forward<std::list<T>>(xs); | |
} | |
template<typename T, typename Container> | |
Container | |
prepend_elem(internal::reuse_container_t, const T& y, Container&& xs) | |
{ | |
xs.resize(size_of_cont(xs) + 1); | |
std::copy(++xs.rbegin(), xs.rend(), xs.rbegin()); | |
*std::begin(xs) = y; | |
return std::forward<Container>(xs); | |
} | |
template<typename T, typename Container> | |
Container | |
prepend_elem(internal::create_new_container_t, const T& y, const Container& xs) | |
{ | |
Container result; | |
internal::prepare_container(result, size_of_cont(xs) + 1); | |
*internal::get_back_inserter(result) = y; | |
std::copy(std::begin(xs), std::end(xs), internal::get_back_inserter(result)); | |
return result; | |
} | |
} // namespace internal | |
// API search type: prepend_elem : (a, [a]) -> [a] | |
// fwd bind count: 1 | |
// Extends a sequence with one element in the front. | |
// prepend_elem([2, 3], 1) == [1, 2, 3] | |
template<typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>, | |
typename T = typename ContainerOut::value_type> | |
ContainerOut | |
prepend_elem(const T& y, Container&& xs) | |
{ | |
return internal::prepend_elem( | |
internal::can_reuse_v<Container>{}, y, std::forward<Container>(xs)); | |
} | |
// API search type: append : ([a], [a]) -> [a] | |
// fwd bind count: 1 | |
// Concatenates two sequences. | |
// append([1, 2], [3, 4, 5]) == [1, 2, 3, 4, 5] | |
template<typename ContainerIn1, | |
typename ContainerIn2 = ContainerIn1, | |
typename ContainerOut = ContainerIn1> | |
ContainerOut | |
append(const ContainerIn1& xs, const ContainerIn2& ys) | |
{ | |
ContainerOut result; | |
internal::prepare_container(result, size_of_cont(xs) + size_of_cont(ys)); | |
std::copy(std::begin(xs), std::end(xs), internal::get_back_inserter(result)); | |
std::copy(std::begin(ys), std::end(ys), internal::get_back_inserter(result)); | |
return result; | |
} | |
// API search type: append_convert : ([a], [a]) -> [a] | |
// fwd bind count: 1 | |
// Same as append, but makes it easier to | |
// use an output container type different from the input container type. | |
template<typename ContainerOut, | |
typename ContainerIn1, | |
typename ContainerIn2 = ContainerIn1> | |
ContainerOut | |
append_convert(const ContainerIn1& xs, const ContainerIn2& ys) | |
{ | |
return append<ContainerIn1, ContainerIn2, ContainerOut>(xs, ys); | |
} | |
// API search type: concat : [[a]] -> [a] | |
// fwd bind count: 0 | |
// Concatenates multiple sequences. | |
// concat([[1, 2], [], [3]]) == [1, 2, 3] | |
// Also known as flatten. | |
template<typename ContainerIn, | |
typename ContainerOut = typename ContainerIn::value_type> | |
ContainerOut | |
concat(const ContainerIn& xss) | |
{ | |
std::size_t length = | |
sum(transform(size_of_cont<typename ContainerIn::value_type>, xss)); | |
ContainerOut result; | |
internal::prepare_container(result, length); | |
using std::begin; | |
using std::end; | |
for (const auto& xs : xss) { | |
result.insert(end(result), begin(xs), end(xs)); | |
} | |
return result; | |
} | |
// API search type: interweave : ([a], [a]) -> [a] | |
// fwd bind count: 1 | |
// Return a sequence that contains elements from the two provided sequences | |
// in alternating order. If one list runs out of items, | |
// appends the items from the remaining list. | |
// interweave([1,3], [2,4]) == [1,2,3,4] | |
// interweave([1,3,5,7], [2,4]) == [1,2,3,4,5,7] | |
// See interleave for interweaving more than two sequences. | |
template<typename Container> | |
Container | |
interweave(const Container& xs, const Container& ys) | |
{ | |
Container result; | |
internal::prepare_container(result, size_of_cont(xs) + size_of_cont(ys)); | |
auto it = internal::get_back_inserter<Container>(result); | |
auto it_xs = std::begin(xs); | |
auto it_ys = std::begin(ys); | |
while (it_xs != std::end(xs) || it_ys != std::end(ys)) { | |
if (it_xs != std::end(xs)) | |
*it = *(it_xs++); | |
if (it_ys != std::end(ys)) | |
*it = *(it_ys++); | |
} | |
return result; | |
} | |
// API search type: unweave : [a] -> ([a], [a]) | |
// fwd bind count: 0 | |
// Puts the elements with an even index into the first list, | |
// and the elements with an odd index into the second list. | |
// Inverse of interweave. | |
// unweave([0,1,2,3]) == ([0,2], [1,3]) | |
// unweave([0,1,2,3,4]) == ([0,2,4], [1,3]) | |
template<typename Container> | |
std::pair<Container, Container> | |
unweave(const Container& xs) | |
{ | |
std::pair<Container, Container> result; | |
if (is_even(size_of_cont(xs))) | |
internal::prepare_container(result.first, size_of_cont(xs) / 2); | |
else | |
internal::prepare_container(result.first, size_of_cont(xs) / 2 + 1); | |
internal::prepare_container(result.second, size_of_cont(xs) / 2); | |
auto it_even = internal::get_back_inserter<Container>(result.first); | |
auto it_odd = internal::get_back_inserter<Container>(result.second); | |
std::size_t counter = 0; | |
for (const auto& x : xs) { | |
if (counter++ % 2 == 0) | |
*it_even = x; | |
else | |
*it_odd = x; | |
} | |
return result; | |
} | |
namespace internal { | |
template<typename Compare, typename T> | |
std::list<T> | |
sort_by(internal::reuse_container_t, Compare comp, std::list<T>&& xs) | |
{ | |
xs.sort(comp); | |
return std::forward<std::list<T>>(xs); | |
} | |
template<typename Compare, typename T> | |
std::list<T> | |
sort_by(internal::create_new_container_t, Compare comp, const std::list<T>& xs) | |
{ | |
auto result = xs; | |
result.sort(comp); | |
return result; | |
} | |
template<typename Compare, typename Container> | |
Container | |
sort_by(internal::reuse_container_t, Compare comp, Container&& xs) | |
{ | |
std::sort(std::begin(xs), std::end(xs), comp); | |
return std::forward<Container>(xs); | |
} | |
template<typename Compare, typename Container> | |
Container | |
sort_by(internal::create_new_container_t, Compare comp, const Container& xs) | |
{ | |
auto result = xs; | |
std::sort(std::begin(result), std::end(result), comp); | |
return result; | |
} | |
} // namespace internal | |
// API search type: sort_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence by given less comparator. | |
template<typename Compare, | |
typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut | |
sort_by(Compare comp, Container&& xs) | |
{ | |
return internal::sort_by( | |
internal::can_reuse_v<Container>{}, comp, std::forward<Container>(xs)); | |
} | |
namespace internal { | |
// workarounds for clang bug 24115 | |
// (std::sort and std::unique with std::function as comp) | |
// https://llvm.org/bugs/show_bug.cgi?id=24115 | |
template<typename F> | |
struct is_less_by_struct | |
{ | |
is_less_by_struct(F f) | |
: f_(f){}; | |
template<typename T> | |
bool operator()(const T& x, const T& y) | |
{ | |
return f_(x) < f_(y); | |
} | |
private: | |
F f_; | |
}; | |
template<typename F> | |
struct is_equal_by_struct | |
{ | |
is_equal_by_struct(F f) | |
: f_(f){}; | |
template<typename T> | |
bool operator()(const T& x, const T& y) | |
{ | |
return f_(x) == f_(y); | |
} | |
private: | |
F f_; | |
}; | |
} | |
// API search type: sort_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence by a given transformer. | |
template<typename F, | |
typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut | |
sort_on(F f, Container&& xs) | |
{ | |
return sort_by(internal::is_less_by_struct<F>(f), | |
std::forward<Container>(xs)); | |
} | |
// API search type: sort : [a] -> [a] | |
// fwd bind count: 0 | |
// Sort a sequence to ascending order using std::less. | |
template<typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut | |
sort(Container&& xs) | |
{ | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return sort_by(std::less<T>(), std::forward<Container>(xs)); | |
} | |
namespace internal { | |
template<typename Compare, typename T> | |
std::list<T> | |
stable_sort_by(internal::reuse_container_t, Compare comp, std::list<T>&& xs) | |
{ | |
xs.sort(comp); // std::list<T>::sort ist already stable. | |
return std::forward<std::list<T>>(xs); | |
} | |
template<typename Compare, typename T> | |
std::list<T> | |
stable_sort_by(internal::create_new_container_t, | |
Compare comp, | |
const std::list<T>& xs) | |
{ | |
auto result = xs; | |
result.sort(comp); // std::list<T>::sort ist already stable. | |
return result; | |
} | |
template<typename Compare, typename Container> | |
Container | |
stable_sort_by(internal::reuse_container_t, Compare comp, Container&& xs) | |
{ | |
std::sort(std::begin(xs), std::end(xs), comp); | |
return std::forward<Container>(xs); | |
} | |
template<typename Compare, typename Container> | |
Container | |
stable_sort_by(internal::create_new_container_t, | |
Compare comp, | |
const Container& xs) | |
{ | |
auto result = xs; | |
std::sort(std::begin(result), std::end(result), comp); | |
return result; | |
} | |
} // namespace internal | |
// API search type: stable_sort_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence stably by given less comparator. | |
template<typename Compare, | |
typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut | |
stable_sort_by(Compare comp, Container&& xs) | |
{ | |
return internal::stable_sort_by( | |
internal::can_reuse_v<Container>{}, comp, std::forward<Container>(xs)); | |
} | |
// API search type: stable_sort_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence stably by given transformer. | |
template<typename F, | |
typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut | |
stable_sort_on(F f, Container&& xs) | |
{ | |
return stable_sort_by(internal::is_less_by_struct<F>(f), | |
std::forward<Container>(xs)); | |
} | |
// API search type: stable_sort : [a] -> [a] | |
// fwd bind count: 0 | |
// Sort a sequence stably to ascending order using std::less. | |
template<typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut | |
stable_sort(Container&& xs) | |
{ | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return stable_sort_by(std::less<T>(), std::forward<Container>(xs)); | |
} | |
namespace internal { | |
template<typename Compare, typename Container> | |
Container | |
partial_sort_by(internal::reuse_container_t, | |
Compare comp, | |
std::size_t count, | |
Container&& xs) | |
{ | |
if (count > xs.size()) { | |
count = xs.size(); | |
} | |
auto middle = std::begin(xs); | |
internal::advance_iterator(middle, count); | |
std::partial_sort(std::begin(xs), middle, std::end(xs), comp); | |
return std::forward<Container>( | |
get_segment(internal::reuse_container_t(), 0, count, xs)); | |
} | |
template<typename Compare, typename Container> | |
Container | |
partial_sort_by(internal::create_new_container_t, | |
Compare comp, | |
std::size_t count, | |
const Container& xs) | |
{ | |
auto result = xs; | |
return partial_sort_by( | |
internal::reuse_container_t(), comp, count, std::move(result)); | |
} | |
} // namespace internal | |
// API search type: partial_sort_by : (((a, a) -> Bool), Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Partially sort a sequence by a given less comparator. | |
// Returns only the sorted segment. | |
template<typename Compare, | |
typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut | |
partial_sort_by(Compare comp, std::size_t count, Container&& xs) | |
{ | |
return internal::partial_sort_by(internal::can_reuse_v<Container>{}, | |
comp, | |
count, | |
std::forward<Container>(xs)); | |
} | |
// API search type: partial_sort_on : ((a -> b), Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Partially sort a sequence by a given transformer. | |
// Returns only the sorted segment. | |
template<typename F, | |
typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut | |
partial_sort_on(F f, std::size_t count, Container&& xs) | |
{ | |
return partial_sort_by( | |
internal::is_less_by_struct<F>(f), count, std::forward<Container>(xs)); | |
} | |
// API search type: partial_sort : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Partially sort a sequence in ascending order using std::less. | |
// Returns only the sorted segment. | |
template<typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut | |
partial_sort(std::size_t count, Container&& xs) | |
{ | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return partial_sort_by(std::less<T>(), count, std::forward<Container>(xs)); | |
} | |
// API search type: nth_element_by : (((a, a) -> Bool), Int, [a]) -> a | |
// fwd bind count: 2 | |
// Return the nth largest element of a sequence by a given less comparator. | |
template<typename Compare, | |
typename Container, | |
typename T = typename Container::value_type> | |
T | |
nth_element_by(Compare comp, std::size_t n, const Container& xs) | |
{ | |
assert(n < xs.size()); | |
auto result = xs; | |
auto middle = std::begin(result); | |
internal::advance_iterator(middle, n); | |
std::nth_element(std::begin(result), middle, std::end(result), comp); | |
return *middle; | |
} | |
// API search type: nth_element_on : ((a -> b), Int, [a]) -> a | |
// fwd bind count: 2 | |
// Return the nth largest element of a sequence by a given transformer. | |
template<typename F, | |
typename Container, | |
typename T = typename Container::value_type> | |
T | |
nth_element_on(F f, std::size_t n, const Container& xs) | |
{ | |
return nth_element_by(internal::is_less_by_struct<F>(f), n, xs); | |
} | |
// API search type: nth_element : (Int, [a]) -> a | |
// fwd bind count: 1 | |
// Return the nth largest element of a sequence using std::less. | |
template<typename Container, typename T = typename Container::value_type> | |
T | |
nth_element(std::size_t n, const Container& xs) | |
{ | |
return nth_element_by(std::less<T>(), n, xs); | |
} | |
namespace internal { | |
template<typename BinaryPredicate, typename Container> | |
Container | |
unique_by(internal::reuse_container_t, BinaryPredicate pred, Container&& xs) | |
{ | |
internal::check_binary_predicate_for_container<BinaryPredicate, Container>(); | |
const auto it_end = std::unique(std::begin(xs), std::end(xs), pred); | |
xs.erase(it_end, std::end(xs)); | |
return std::forward<Container>(xs); | |
} | |
template<typename BinaryPredicate, typename Container> | |
Container | |
unique_by(internal::create_new_container_t, | |
BinaryPredicate pred, | |
const Container& xs) | |
{ | |
auto result = xs; | |
return unique_by(internal::reuse_container_t(), pred, std::move(result)); | |
} | |
} // namespace internal | |
// API search type: unique_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Like unique, eliminates all but the first element | |
// from every consecutive group of equivalent elements from the sequence; | |
// but with a user supplied equality predicate. | |
// See nub_by for making elements globally unique in a sequence. | |
// O(n) | |
template<typename BinaryPredicate, | |
typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut | |
unique_by(BinaryPredicate pred, Container&& xs) | |
{ | |
return internal::unique_by( | |
internal::can_reuse_v<Container>{}, pred, std::forward<Container>(xs)); | |
} | |
// API search type: unique_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Like unique, eliminates all but the first element | |
// from every consecutive group of equivalent elements from the sequence; | |
// but with a user supplied transformation (e.g. getter). | |
// See nub_on for making elements globally unique in a sequence. | |
// O(n) | |
// Also known as drop_repeats. | |
template<typename F, | |
typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut | |
unique_on(F f, Container&& xs) | |
{ | |
return unique_by(internal::is_equal_by_struct<F>(f), | |
std::forward<Container>(xs)); | |
} | |
// API search type: unique : [a] -> [a] | |
// fwd bind count: 0 | |
// Eliminates all but the first element | |
// from every consecutive group of equivalent elements from the sequence. | |
// unique([1,2,2,3,2]) == [1,2,3,2] | |
// See nub for making elements globally unique in a sequence. | |
// O(n) | |
template<typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut | |
unique(Container&& xs) | |
{ | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return unique_on(identity<T>, std::forward<Container>(xs)); | |
} | |
// API search type: intersperse : (a, [a]) -> [a] | |
// fwd bind count: 1 | |
// Insert a value between all adjacent values in a sequence. | |
// intersperse(0, [1, 2, 3]) == [1, 0, 2, 0, 3] | |
// Also known as interpose. | |
template<typename Container, typename X = typename Container::value_type> | |
Container | |
intersperse(const X& value, const Container& xs) | |
{ | |
if (xs.empty()) | |
return Container(); | |
if (size_of_cont(xs) == 1) | |
return xs; | |
Container result; | |
internal::prepare_container( | |
result, std::max<std::size_t>(0, size_of_cont(xs) * 2 - 1)); | |
auto it = internal::get_back_inserter(result); | |
for_each(std::begin(xs), --std::end(xs), [&value, &it](const X& x) { | |
*it = x; | |
*it = value; | |
}); | |
*it = xs.back(); | |
return result; | |
} | |
// API search type: join : ([a], [[a]]) -> [a] | |
// fwd bind count: 1 | |
// Inserts a separator sequence in between the elements | |
// of a sequence of sequences and concatenates the result. | |
// Also known as intercalate or implode. | |
// join(", ", ["a", "bee", "cee"]) == "a, bee, cee" | |
// join([0, 0], [[1], [2], [3, 4]]) == [1, 0, 0, 2, 0, 0, 3, 4] | |
template<typename Container, typename X = typename Container::value_type> | |
X | |
join(const X& separator, const Container& xs) | |
{ | |
return concat(intersperse(separator, xs)); | |
} | |
// API search type: join_elem : (a, [[a]]) -> [a] | |
// fwd bind count: 1 | |
// Inserts a separator in between the elements | |
// of a sequence of sequences and concatenates the result. | |
// Also known as intercalate_elem. | |
// join_elem(';', ["a", "bee", "cee"]) == "a;bee;cee" | |
// join_elem(0, [[1], [2], [3, 4]]) == [1, 0, 2, 0, 3, 4]] | |
template<typename Container, | |
typename Inner = typename Container::value_type, | |
typename X = typename Inner::value_type> | |
Inner | |
join_elem(const X& separator, const Container& xs) | |
{ | |
return join(Inner(1, separator), xs); | |
} | |
// API search type: is_elem_of_by : ((a -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if at least one element of the container fulfils a predicate. | |
// is_elem_of_by((==), [1,2,3]) == true | |
template<typename UnaryPredicate, typename Container> | |
bool | |
is_elem_of_by(UnaryPredicate pred, const Container& xs) | |
{ | |
return std::find_if(std::begin(xs), std::end(xs), pred) != std::end(xs); | |
} | |
// API search type: is_elem_of : (a, [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if an element is a member of a container. | |
// is_elem_of(2, [1,2,3]) == true | |
// Also known as flip(contains). | |
template<typename Container> | |
bool | |
is_elem_of(const typename Container::value_type& x, const Container& xs) | |
{ | |
return is_elem_of_by(is_equal_to(x), xs); | |
} | |
// API search type: nub_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Makes the elements in a container unique with respect to a predicate | |
// nub_by((==), [1,2,2,3,2]) == [1,2,3] | |
// O(n^2) | |
template<typename Container, typename BinaryPredicate> | |
Container | |
nub_by(BinaryPredicate p, const Container& xs) | |
{ | |
Container result; | |
auto itOut = internal::get_back_inserter(result); | |
for (const auto& x : xs) { | |
auto eqToX = bind_1st_of_2(p, x); | |
if (!is_elem_of_by(eqToX, result)) { | |
*itOut = x; | |
} | |
} | |
return result; | |
} | |
// API search type: nub_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Makes the elements in a container unique | |
// with respect to their function value. | |
// nub_on((mod 10), [12,32,15]) == [12,15] | |
// O(n^2) | |
template<typename Container, typename F> | |
Container | |
nub_on(F f, const Container& xs) | |
{ | |
return nub_by(is_equal_by(f), xs); | |
} | |
// API search type: nub : [a] -> [a] | |
// fwd bind count: 0 | |
// Makes the elements in a container unique. | |
// nub([1,2,2,3,2]) == [1,2,3] | |
// O(n^2) | |
// Also known as distinct. | |
template<typename Container> | |
Container | |
nub(const Container& xs) | |
{ | |
typedef typename Container::value_type T; | |
return nub_by(std::equal_to<T>(), xs); | |
} | |
// API search type: all_unique_by_eq : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all elements in a container are unique | |
// with respect to a predicate. | |
// Returns true for empty containers. | |
// O(n^2) | |
template<typename Container, typename BinaryPredicate> | |
bool | |
all_unique_by_eq(BinaryPredicate p, const Container& xs) | |
{ | |
internal::check_binary_predicate_for_container<BinaryPredicate, Container>(); | |
return size_of_cont(nub_by(p, xs)) == size_of_cont(xs); | |
} | |
// API search type: all_unique_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all elements in a container are unique | |
// with respect to their function values. | |
// Returns true for empty containers. | |
// O(n^2) | |
template<typename Container, typename F> | |
bool | |
all_unique_on(F f, const Container& xs) | |
{ | |
return all_unique_by_eq(is_equal_by(f), xs); | |
} | |
// API search type: all_unique : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if all elements in a container are unique. | |
// Returns true for empty containers. | |
// O(n^2) | |
template<typename Container> | |
bool | |
all_unique(const Container& xs) | |
{ | |
typedef typename Container::value_type T; | |
auto comp = std::equal_to<T>(); | |
return all_unique_by_eq(comp, xs); | |
} | |
// API search type: is_strictly_sorted_by : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is strictly sorted using a predicate. | |
// comp(a, b) must return true only if a < b. | |
// O(n) | |
template<typename Container, typename Compare> | |
bool | |
is_strictly_sorted_by(Compare comp, const Container& xs) | |
{ | |
internal::check_compare_for_container<Compare, Container>(); | |
if (size_of_cont(xs) < 2) | |
return true; | |
auto it1 = std::begin(xs); | |
for (auto it2 = it1 + 1; it2 < std::end(xs); ++it1, ++it2) | |
if (!internal::invoke(comp, *it1, *it2)) | |
return false; | |
return true; | |
} | |
// API search type: is_strictly_sorted_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is strictly sorted using a transformer. | |
// O(n) | |
template<typename Container, typename F> | |
bool | |
is_strictly_sorted_on(F f, const Container& xs) | |
{ | |
return is_strictly_sorted_by(is_less_by(f), xs); | |
} | |
// API search type: is_strictly_sorted : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if a container already is strictly sorted | |
// in ascending order using std::less. | |
// O(n) | |
template<typename Container> | |
bool | |
is_strictly_sorted(const Container& xs) | |
{ | |
typedef typename Container::value_type T; | |
auto comp = std::less<T>(); | |
return is_strictly_sorted_by(comp, xs); | |
} | |
// API search type: is_sorted_by : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is sorted using a predicate. | |
// comp(a, b) must return true only if a < b. | |
// O(n) | |
template<typename Container, typename Compare> | |
bool | |
is_sorted_by(Compare comp, const Container& xs) | |
{ | |
internal::check_compare_for_container<Compare, Container>(); | |
if (size_of_cont(xs) < 2) | |
return true; | |
auto it1 = std::begin(xs); | |
for (auto it2 = it1 + 1; it2 < std::end(xs); ++it1, ++it2) | |
if (internal::invoke(comp, *it2, *it1)) | |
return false; | |
return true; | |
} | |
// API search type: is_sorted_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is strictly sorted using a transformer. | |
// O(n) | |
template<typename Container, typename F> | |
bool | |
is_sorted_on(F f, const Container& xs) | |
{ | |
return is_sorted_by(is_less_by(f), xs); | |
} | |
// API search type: is_sorted : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if a container already is sorted | |
// in ascending order using std::less. | |
// O(n) | |
template<typename Container> | |
bool | |
is_sorted(const Container& xs) | |
{ | |
typedef typename Container::value_type T; | |
auto comp = std::less<T>(); | |
return is_sorted_by(comp, xs); | |
} | |
// API search type: is_prefix_of : ([a], [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a containers starts with a token. | |
// is_prefix_of("Fun", "FunctionalPlus") == true | |
template<typename Container> | |
bool | |
is_prefix_of(const Container& token, const Container& xs) | |
{ | |
if (size_of_cont(token) > size_of_cont(xs)) | |
return false; | |
return get_segment(0, size_of_cont(token), xs) == token; | |
} | |
// API search type: is_suffix_of : ([a], [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a containers contains a token as a segment. | |
// is_suffix_of("us", "FunctionalPlus") == true | |
template<typename Container> | |
bool | |
is_suffix_of(const Container& token, const Container& xs) | |
{ | |
if (size_of_cont(token) > size_of_cont(xs)) | |
return false; | |
return get_segment(size_of_cont(xs) - size_of_cont(token), | |
size_of_cont(xs), | |
xs) == token; | |
} | |
// API search type: all_by : ((a -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a containers contains a token as a segment. | |
// all_by(is_even, [2, 4, 6]) == true | |
// Returns true for empty containers. | |
template<typename UnaryPredicate, typename Container> | |
bool | |
all_by(UnaryPredicate p, const Container& xs) | |
{ | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
return std::all_of(std::begin(xs), std::end(xs), p); | |
} | |
// API search type: all : [Bool] -> Bool | |
// fwd bind count: 0 | |
// Checks if all values in a container evaluate to true. | |
// all([true, false, true]) == false | |
// Returns true for empty containers. | |
template<typename Container> | |
bool | |
all(const Container& xs) | |
{ | |
typedef typename Container::value_type T; | |
return all_by(identity<T>, xs); | |
} | |
// API search type: all_the_same_by : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all values in a container are equal using a binary predicate. | |
// Returns true for empty containers. | |
template<typename Container, typename BinaryPredicate> | |
bool | |
all_the_same_by(BinaryPredicate p, const Container& xs) | |
{ | |
internal::check_binary_predicate_for_container<BinaryPredicate, Container>(); | |
if (size_of_cont(xs) < 2) | |
return true; | |
auto unaryPredicate = bind_1st_of_2(p, xs.front()); | |
return all_by(unaryPredicate, xs); | |
} | |
// API search type: all_the_same_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all values in a container are equal using a transformer. | |
// Returns true for empty containers. | |
template<typename Container, typename F> | |
bool | |
all_the_same_on(F f, const Container& xs) | |
{ | |
if (size_of_cont(xs) < 2) | |
return true; | |
auto unaryPredicate = is_equal_by_to(f, f(xs.front())); | |
return all_by(unaryPredicate, xs); | |
} | |
// API search type: all_the_same : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if all values in a container are equal. | |
// Returns true for empty containers. | |
template<typename Container> | |
bool | |
all_the_same(const Container& xs) | |
{ | |
typedef typename Container::value_type T; | |
auto binaryPredicate = std::equal_to<T>(); | |
return all_the_same_by(binaryPredicate, xs); | |
} | |
// API search type: numbers_step : (a, a, a) -> [a] | |
// fwd bind count: 2 | |
// Return a sequence of numbers using a specific step. | |
// numbers_step(2, 9, 2) == [2, 4, 6, 8] | |
template<typename T, typename ContainerOut = std::vector<T>> | |
ContainerOut | |
numbers_step(const T start, const T end, const T step) | |
{ | |
ContainerOut result; | |
if ((step > 0 && start >= end) || (step < 0 && start <= end) || step == 0) { | |
return result; | |
} | |
std::size_t size = static_cast<std::size_t>((end - start) / step); | |
internal::prepare_container(result, size); | |
auto it = internal::get_back_inserter<ContainerOut>(result); | |
for (T x = start; x < end; x += step) | |
*it = x; | |
return result; | |
} | |
// API search type: numbers : (a, a) -> [a] | |
// fwd bind count: 1 | |
// Return an ascending sequence of numbers.. | |
// Also known as range. | |
// numbers(2, 9) == [2, 3, 4, 5, 6, 7, 8] | |
template<typename T, typename ContainerOut = std::vector<T>> | |
ContainerOut | |
numbers(const T start, const T end) | |
{ | |
return numbers_step<T, ContainerOut>(start, end, 1); | |
} | |
// API search type: singleton_seq : a -> [a] | |
// fwd bind count: 0 | |
// Construct a sequence containing a single value. | |
// singleton_seq(3) == [3]. | |
template<typename T, typename ContainerOut = std::vector<T>> | |
ContainerOut | |
singleton_seq(const T& x) | |
{ | |
return ContainerOut(1, x); | |
} | |
// API search type: all_idxs : [a] -> [Int] | |
// fwd bind count: 0 | |
// Returns a vector containing all valid indices of sequence xs. | |
// all_idxs([6,4,7,6]) == [0,1,2,3] | |
template<typename Container> | |
std::vector<std::size_t> | |
all_idxs(const Container& xs) | |
{ | |
return numbers<std::size_t>(0, size_of_cont(xs)); | |
} | |
// API search type: init : [a] -> [a] | |
// fwd bind count: 0 | |
// init([0,1,2,3]) == [0,1,2] | |
// Unsafe! xs must be non-empty. | |
template<typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut | |
init(Container&& xs) | |
{ | |
assert(!is_empty(xs)); | |
return get_segment(0, size_of_cont(std::forward<Container>(xs)) - 1, xs); | |
} | |
// API search type: tail : [a] -> [a] | |
// fwd bind count: 0 | |
// Drops the first element of a container, keeps the rest. Unsafe! | |
// tail([0,1,2,3]) == [1,2,3] | |
// Unsafe! xs must be non-empty. | |
template<typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut | |
tail(Container&& xs) | |
{ | |
assert(!is_empty(xs)); | |
return get_segment(1, size_of_cont(std::forward<Container>(xs)), xs); | |
} | |
// API search type: head : [a] -> a | |
// fwd bind count: 0 | |
// Return the first element of a container. | |
// head([0,1,2,3]) == 0 | |
// Unsafe! xs must be non-empty. | |
template<typename Container> | |
typename Container::value_type | |
head(const Container& xs) | |
{ | |
assert(!is_empty(xs)); | |
return xs.front(); | |
} | |
// API search type: last : [a] -> a | |
// fwd bind count: 0 | |
// Return the last element of a container. | |
// last([0,1,2,3]) == 3 | |
// Unsafe! xs must be non-empty. | |
template<typename Container> | |
typename Container::value_type | |
last(const Container& xs) | |
{ | |
assert(!is_empty(xs)); | |
return xs.back(); | |
} | |
// API search type: mean_stddev : [a] -> (a, a) | |
// fwd bind count: 0 | |
// Calculates the mean and the population standard deviation. | |
// mean_stddev([4, 8]) == (6, 2) | |
// mean_stddev([1, 3, 7, 4]) == (3.75, 2.5) | |
// xs must be non-empty. | |
template<typename Result, typename Container> | |
std::pair<Result, Result> | |
mean_stddev(const Container& xs) | |
{ | |
assert(size_of_cont(xs) != 0); | |
// http://stackoverflow.com/a/7616783/1866775 | |
Result sum = static_cast<Result>(internal::accumulate( | |
xs.begin(), xs.end(), static_cast<typename Container::value_type>(0))); | |
Result mean = sum / static_cast<Result>(xs.size()); | |
std::vector<Result> diff(xs.size()); | |
std::transform( | |
xs.begin(), xs.end(), diff.begin(), [mean](Result x) { return x - mean; }); | |
Result sq_sum = std::inner_product( | |
diff.begin(), diff.end(), diff.begin(), static_cast<Result>(0)); | |
Result stddev = std::sqrt(sq_sum / static_cast<Result>(xs.size())); | |
return std::make_pair(mean, stddev); | |
} | |
// API search type: count_occurrences_by : ((a -> b), [a]) -> Map b Int | |
// fwd bind count: 1 | |
// Returns a discrete frequency distribution of the elements in a container | |
// applying a specific transformer. | |
// count_occurrences_by(floor, [1.1, 2.3, 2.7, 3.6, 2.4]) == [(1, 1), (2, 3), | |
// (3, 1)] O(n) | |
template<typename F, typename ContainerIn> | |
auto | |
count_occurrences_by(F f, const ContainerIn& xs) | |
{ | |
using In = typename ContainerIn::value_type; | |
using MapOut = | |
std::map<std::decay_t<internal::invoke_result_t<F, In>>, std::size_t>; | |
internal::trigger_static_asserts<internal::unary_function_tag, | |
F, | |
typename ContainerIn::value_type>(); | |
MapOut result; | |
for (const auto& x : xs) { | |
++result[internal::invoke(f, x)]; | |
} | |
return result; | |
} | |
// API search type: count_occurrences : [a] -> Map a Int | |
// fwd bind count: 0 | |
// Returns a discrete frequency distribution of the elements in a container | |
// applying a specific transformer. | |
// Can be used to create a histogram. | |
// count_occurrences([1,2,2,3,2]) == [(1, 1), (2, 3), (3, 1)] | |
// O(n) | |
template<typename ContainerIn, | |
typename MapOut = | |
typename std::map<typename ContainerIn::value_type, std::size_t>> | |
MapOut | |
count_occurrences(const ContainerIn& xs) | |
{ | |
return count_occurrences_by(identity<typename ContainerIn::value_type>, xs); | |
} | |
// API search type: lexicographical_less_by : (((a, a) -> Bool), [a], [a]) -> | |
// Bool fwd bind count: 2 Lexicographical less-than comparison using a specific | |
// predicate. lexicographical_less_by((<), [0,1,2,2,4,5], [0,1,2,3,4,5]) == true | |
// lexicographical_less_by((<), "012245", "012345") == true | |
// lexicographical_less_by((<), "01234", "012345") == true | |
// lexicographical_less_by((<), "012345", "01234") == false | |
// lexicographical_less_by((<), "012345", "012345") == false | |
template<typename Container, typename BinaryPredicate> | |
bool | |
lexicographical_less_by(BinaryPredicate p, | |
const Container& xs, | |
const Container& ys) | |
{ | |
internal::check_binary_predicate_for_container<BinaryPredicate, Container>(); | |
auto itXs = std::begin(xs); | |
auto itYs = std::begin(ys); | |
while (itXs != std::end(xs) && itYs != std::end(ys)) { | |
if (internal::invoke(p, *itXs, *itYs)) { | |
return true; | |
} | |
if (internal::invoke(p, *itYs, *itXs)) { | |
return false; | |
} | |
++itXs; | |
++itYs; | |
} | |
if (size_of_cont(xs) < size_of_cont(ys)) { | |
return true; | |
} | |
return false; | |
} | |
// API search type: lexicographical_less : ([a], [a]) -> Bool | |
// fwd bind count: 1 | |
// Lexicographical less-than comparison. | |
// lexicographical_less([0,1,2,2,4,5], [0,1,2,3,4,5]) == true | |
// lexicographical_less("012245", "012345") == true | |
// lexicographical_less("01234", "012345") == true | |
// lexicographical_less("012345", "01234") == false | |
// lexicographical_less("012345", "012345") == false | |
template<typename Container> | |
bool | |
lexicographical_less(const Container& xs, const Container& ys) | |
{ | |
return lexicographical_less_by( | |
is_less<typename Container::value_type>, xs, ys); | |
} | |
// API search type: lexicographical_sort : [[a]] -> [[a]] | |
// fwd bind count: 0 | |
// sort by lexicographical_less | |
template<typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut | |
lexicographical_sort(Container&& xs) | |
{ | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return sort_by(lexicographical_less<T>, std::forward<Container>(xs)); | |
} | |
// API search type: replicate : (Int, a) -> [a] | |
// fwd bind count: 1 | |
// Create a sequence containing x n times. | |
// replicate(3, 1) == [1, 1, 1] | |
template<typename T, typename ContainerOut = std::vector<T>> | |
ContainerOut | |
replicate(std::size_t n, const T& x) | |
{ | |
return ContainerOut(n, x); | |
} | |
namespace internal { | |
template<typename UnaryPredicate, typename T> | |
T | |
instead_of_if(internal::reuse_container_t, | |
UnaryPredicate pred, | |
const T& alt, | |
T&& x) | |
{ | |
if (internal::invoke(pred, x)) | |
return alt; | |
else | |
return std::forward<T>(x); | |
} | |
template<typename UnaryPredicate, typename T> | |
T | |
instead_of_if(internal::create_new_container_t, | |
UnaryPredicate pred, | |
const T& alt, | |
const T& x) | |
{ | |
if (internal::invoke(pred, x)) | |
return alt; | |
else | |
return x; | |
} | |
} // namespace internal | |
// API search type: instead_of_if : ((a -> Bool), a, a) -> a | |
// fwd bind count: 2 | |
// Return alt if pred(x), otherwise x itself. | |
template<typename UnaryPredicate, typename T, typename TAlt> | |
auto | |
instead_of_if(UnaryPredicate pred, const TAlt& alt, T&& x) | |
{ | |
return internal::instead_of_if( | |
internal::can_reuse_v<T>{}, pred, alt, std::forward<T>(x)); | |
} | |
// API search type: instead_of_if_empty : ((a -> Bool), [a], [a]) -> [a] | |
// fwd bind count: 2 | |
// Return alt if xs is empty, otherwise xs itself. | |
template<typename Container, | |
typename ContainerAlt, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut | |
instead_of_if_empty(const ContainerAlt& alt, Container&& xs) | |
{ | |
return instead_of_if(is_empty<internal::remove_const_and_ref_t<Container>>, | |
alt, | |
std::forward<Container>(xs)); | |
} | |
} // namespace fplus |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. | |
// https://github.com/Dobiasd/FunctionalPlus | |
// Distributed under the Boost Software License, Version 1.0. | |
// (See accompanying file LICENSE_1_0.txt or copy at | |
// http://www.boost.org/LICENSE_1_0.txt) | |
#pragma once | |
#include <fplus/compare.hpp> | |
#include <fplus/composition.hpp> | |
#include <fplus/container_traits.hpp> | |
#include <fplus/maybe.hpp> | |
#include <fplus/internal/container_common.hpp> | |
#include <fplus/internal/invoke.hpp> | |
#include <fplus/internal/meta.hpp> | |
#include <algorithm> | |
#include <cassert> | |
#include <cmath> | |
#include <cstddef> | |
#include <iterator> | |
#include <numeric> | |
namespace fplus { | |
namespace internal { | |
template <typename UnaryPredicate, typename Container> | |
void check_unary_predicate_for_container() | |
{ | |
internal::check_unary_predicate_for_type<UnaryPredicate, | |
typename Container::value_type>(); | |
} | |
template <typename F, typename Container> | |
void check_index_with_type_predicate_for_container() | |
{ | |
typedef typename Container::value_type T; | |
internal::trigger_static_asserts<internal::binary_function_tag, F, std::size_t, T>(); | |
static_assert(std::is_convertible< | |
internal::invoke_result_t<F, std::size_t, T>, bool>::value, | |
"Function must return bool."); | |
} | |
template <typename Compare, typename Container> | |
void check_compare_for_container() | |
{ | |
typedef typename Container::value_type T; | |
internal::trigger_static_asserts<internal::binary_predicate_tag, Compare, T, T>(); | |
} | |
template <typename BinaryPredicate, typename Container> | |
void check_binary_predicate_for_container() | |
{ | |
typedef typename Container::value_type T; | |
internal::trigger_static_asserts<internal::binary_predicate_tag, BinaryPredicate, T, T>(); | |
} | |
// PrepareContainer and BackInserter are overloaded | |
// to increase performance on std::vector and std::string | |
// by using std::vector<T>::reserve | |
// and std::back_inserter instead of std::back_inserter. | |
// In VC2015, release mode, Celsius W520 Xeon | |
// this leads to an increase in performance of about a factor of 3 | |
// for transform. | |
template <typename C> | |
void prepare_container(const std::basic_string<C, std::char_traits<C>, | |
std::allocator<C>>& ys, | |
std::size_t size) | |
{ | |
ys.reserve(size); | |
} | |
template <typename Y> | |
void prepare_container(std::vector<Y>& ys, std::size_t size) | |
{ | |
ys.reserve(size); | |
} | |
template <typename T, std::size_t N> | |
void prepare_container(std::array<T, N>&, std::size_t size) | |
{ | |
assert(size == N); | |
unused(size); | |
} | |
template <typename Y> | |
void prepare_container(std::unordered_set<Y>& ys, std::size_t size) | |
{ | |
ys.reserve(size); | |
} | |
template <typename Key, typename T> | |
void prepare_container(std::unordered_map<Key, T>& ys, std::size_t size) | |
{ | |
ys.reserve(size); | |
} | |
template <typename Y> | |
void prepare_container(std::unordered_multiset<Y>& ys, std::size_t size) | |
{ | |
ys.reserve(size); | |
} | |
template <typename Key, typename T> | |
void prepare_container(std::unordered_multimap<Key, T>& ys, std::size_t size) | |
{ | |
ys.reserve(size); | |
} | |
template <typename Container> | |
void prepare_container(Container&, std::size_t) | |
{ | |
} | |
template <typename Container> | |
std::back_insert_iterator<Container> get_back_inserter(std::string& ys) | |
{ | |
return std::back_inserter(ys); | |
} | |
template <typename Container, typename Y> | |
std::back_insert_iterator<Container> get_back_inserter(std::vector<Y>& ys) | |
{ | |
return std::back_inserter(ys); | |
} | |
template <typename Container, typename Y> | |
std::back_insert_iterator<Container> get_back_inserter(std::list<Y>& ys) | |
{ | |
return std::back_inserter(ys); | |
} | |
template <typename Container, typename Y> | |
std::back_insert_iterator<Container> get_back_inserter(std::deque<Y>& ys) | |
{ | |
return std::back_inserter(ys); | |
} | |
// Avoid self-assignment. | |
template <typename T> | |
void assign(T& x, T&& y) | |
{ | |
if (&x != &y) | |
x = std::move(y); | |
} | |
template <typename T, std::size_t N> | |
struct array_back_insert_iterator : public std::back_insert_iterator<std::array<T, N>> { | |
typedef std::back_insert_iterator<std::array<T, N>> base_type; | |
explicit array_back_insert_iterator(std::array<T, N>& arr) | |
: base_type(arr) | |
, arr_ptr_(&arr) | |
, pos_(0) | |
{ | |
} | |
array_back_insert_iterator(const array_back_insert_iterator<T, N>& other) | |
: base_type(*other.arr_ptr_) | |
, arr_ptr_(other.arr_ptr_) | |
, pos_(other.pos_) | |
{ | |
} | |
array_back_insert_iterator<T, N>& operator=(const array_back_insert_iterator<T, N>& other) | |
{ | |
arr_ptr_ = other.arr_ptr_; | |
pos_ = other.pos_; | |
return *this; | |
} | |
~array_back_insert_iterator() | |
{ | |
assert(pos_ == 0 || pos_ == N); | |
} | |
array_back_insert_iterator<T, N>& operator=(const T& x) | |
{ | |
assert(pos_ < N); | |
(*arr_ptr_)[pos_] = x; | |
++pos_; | |
return *this; | |
} | |
array_back_insert_iterator<T, N>& operator=(T&& x) | |
{ | |
assert(pos_ < N); | |
assign((*arr_ptr_)[pos_], std::move(x)); | |
++pos_; | |
return *this; | |
} | |
array_back_insert_iterator<T, N>& operator*() { return *this; } | |
array_back_insert_iterator<T, N>& operator++() { return *this; } | |
array_back_insert_iterator<T, N> operator++(int) { return *this; } | |
private: | |
std::array<T, N>* arr_ptr_; | |
std::size_t pos_; | |
}; | |
#if defined(_MSC_VER) && _MSC_VER >= 1900 | |
template <typename T, std::size_t N> | |
struct std::_Is_checked_helper<array_back_insert_iterator<T, N>> | |
: public true_type { // mark array_back_insert_iterator as checked | |
}; | |
#endif | |
template <typename Container, typename Y, std::size_t N> | |
array_back_insert_iterator<Y, N> get_back_inserter(std::array<Y, N>& ys) | |
{ | |
return array_back_insert_iterator<Y, N>(ys); | |
} | |
template <typename Container> | |
std::insert_iterator<Container> get_back_inserter(Container& ys) | |
{ | |
return std::inserter(ys, std::end(ys)); | |
} | |
template <typename Iterator> | |
void advance_iterator(Iterator& it, std::size_t distance) | |
{ | |
std::advance(it, | |
static_cast<typename Iterator::difference_type>(distance)); | |
} | |
template <typename T> | |
void advance_iterator(T*& it, std::size_t distance) | |
{ | |
it += static_cast<std::ptrdiff_t>(distance); | |
} | |
template <typename Iterator> | |
Iterator add_to_iterator(Iterator it, std::size_t distance = 1) | |
{ | |
return std::next(it, | |
static_cast<typename Iterator::difference_type>(distance)); | |
} | |
// GCC 4.9 does not support std::rbegin, std::rend and std::make_reverse_iterator | |
template <typename Iterator> | |
std::reverse_iterator<Iterator> make_reverse_iterator(Iterator it) | |
{ | |
return std::reverse_iterator<Iterator>(it); | |
} | |
} // namespace internal | |
// API search type: is_even : Int -> Bool | |
// fwd bind count: 0 | |
// Checks if x is even. | |
template <typename X> | |
bool is_even(X x) | |
{ | |
static_assert(std::is_integral<X>::value, "type must be integral"); | |
return x % 2 == 0; | |
} | |
// API search type: is_odd : Int -> Bool | |
// fwd bind count: 0 | |
// Checks if x is odd. | |
template <typename X> | |
bool is_odd(X x) | |
{ | |
static_assert(std::is_integral<X>::value, "type must be integral"); | |
return x % 2 != 0; | |
} | |
// API search type: is_empty : [a] -> Bool | |
// fwd bind count: 0 | |
// Returns true if the container holds no elements. | |
// is_empty([1, 2]) == false | |
// is_empty([]) == true | |
template <typename Container> | |
bool is_empty(const Container& xs) | |
{ | |
return xs.empty(); | |
} | |
// API search type: is_not_empty : [a] -> Bool | |
// fwd bind count: 0 | |
// Returns true if the container holds at least one element. | |
// is_not_empty([1, 2]) == true | |
template <typename Container> | |
bool is_not_empty(const Container& xs) | |
{ | |
return !is_empty(xs); | |
} | |
// API search type: size_of_cont : [a] -> Int | |
// fwd bind count: 0 | |
// Returns the number of elements in the given container. | |
// size_of_cont([3, 4]) == 2 | |
template <typename Container> | |
std::size_t size_of_cont(const Container& xs) | |
{ | |
return xs.size(); | |
} | |
// API search type: convert : a -> b | |
// fwd bind count: 0 | |
// Converts one type of element into another. | |
template <typename Dest, typename Source> | |
Dest convert(const Source& x) | |
{ | |
return Dest(x); | |
} | |
// API search type: convert_elems : [a] -> [b] | |
// fwd bind count: 0 | |
// Converts all elements in a sequence to a different type. | |
// convert_elems<NewT>([1, 2, 3]) == [NewT(1), NewT(2), NewT(3)] | |
template <typename NewT, typename ContainerIn, | |
typename ContainerOut = typename internal::same_cont_new_t<ContainerIn, NewT, 0>::type> | |
ContainerOut convert_elems(const ContainerIn& xs) | |
{ | |
static_assert(std::is_constructible<NewT, | |
typename ContainerIn::value_type>::value, | |
"Elements not convertible."); | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it = internal::get_back_inserter<ContainerOut>(ys); | |
// using 'for (const auto& x ...)' is even for ints as fast as | |
// using 'for (int x ...)' (GCC, O3), so there is no need to | |
// check if the type is fundamental and then dispatch accordingly. | |
for (const auto& x : xs) { | |
*it = convert<NewT>(x); | |
} | |
return ys; | |
} | |
// API search type: convert_container : [a] -> [a] | |
// fwd bind count: 0 | |
// Change the type of the container | |
// while keeping the elements in the sequence the same. | |
// convert_container([1, 2, 3]) == [1, 2, 3] | |
// Useful for example if you want to convert an std::list to an std::vector. | |
template <typename ContainerOut, typename ContainerIn> | |
ContainerOut convert_container(const ContainerIn& xs) | |
{ | |
typedef typename ContainerIn::value_type SourceElem; | |
typedef typename ContainerOut::value_type DestElem; | |
static_assert(std::is_same<DestElem, SourceElem>::value, | |
"Source and dest container must have the same value_type"); | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto itOut = internal::get_back_inserter<ContainerOut>(ys); | |
std::copy(std::begin(xs), std::end(xs), itOut); | |
return ys; | |
} | |
// API search type: convert_container_and_elems : [a] -> [b] | |
// fwd bind count: 0 | |
// Converts between different containers and elements. | |
// Dest elements are allowed to have explicit constructors. | |
// convert([1, 2, 3]) == [1, 2, 3] | |
template <typename ContainerOut, typename ContainerIn> | |
ContainerOut convert_container_and_elems(const ContainerIn& xs) | |
{ | |
static_assert(std::is_convertible<typename ContainerIn::value_type, | |
typename ContainerOut::value_type>::value, | |
"Elements not convertible."); | |
typedef typename ContainerOut::value_type DestElem; | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it = internal::get_back_inserter<ContainerOut>(ys); | |
for (const auto& x : xs) { | |
*it = convert<DestElem>(x); | |
} | |
return ys; | |
} | |
namespace internal { | |
template <typename Container> | |
Container get_segment(internal::reuse_container_t, | |
std::size_t idx_begin, std::size_t idx_end, Container&& xs) | |
{ | |
idx_end = std::min(idx_end, size_of_cont(xs)); | |
if (idx_end <= idx_begin) { | |
xs.clear(); | |
return std::forward<Container>(xs); | |
} | |
auto itBegin = std::begin(xs); | |
internal::advance_iterator(itBegin, idx_begin); | |
auto itEnd = itBegin; | |
internal::advance_iterator(itEnd, idx_end - idx_begin); | |
xs.erase(std::copy(itBegin, itEnd, std::begin(xs)), std::end(xs)); | |
return std::forward<Container>(xs); | |
} | |
template <typename Container> | |
Container get_segment(internal::create_new_container_t, | |
std::size_t idx_begin, std::size_t idx_end, const Container& xs) | |
{ | |
idx_end = std::min(idx_end, size_of_cont(xs)); | |
if (idx_end <= idx_begin) { | |
return {}; | |
} | |
Container result; | |
auto itBegin = std::begin(xs); | |
internal::advance_iterator(itBegin, idx_begin); | |
auto itEnd = itBegin; | |
internal::advance_iterator(itEnd, idx_end - idx_begin); | |
std::copy(itBegin, itEnd, internal::get_back_inserter(result)); | |
return result; | |
} | |
} // namespace internal | |
// API search type: get_segment : (Int, Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Return a defined segment from the sequence. | |
// get_segment(2, 5, [0,1,2,3,4,5,6,7,8]) == [2,3,4] | |
// get_segment(2, 15, [0,1,2,3,4,5,6,7,8]) == [2,3,4,5,6,7,8] | |
// get_segment(5, 2, [0,1,2,3,4,5,6,7,8]) == [] | |
// Also known as slice. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut get_segment(std::size_t idx_begin, std::size_t idx_end, Container&& xs) | |
{ | |
return internal::get_segment(internal::can_reuse_v<Container> {}, | |
idx_begin, idx_end, std::forward<Container>(xs)); | |
} | |
namespace internal { | |
template <typename ContainerToken, typename Container> | |
Container set_segment(internal::reuse_container_t, | |
std::size_t idx_begin, const ContainerToken& token, Container&& xs) | |
{ | |
assert(idx_begin + size_of_cont(token) < size_of_cont(xs)); | |
auto itBegin = std::begin(xs); | |
internal::advance_iterator(itBegin, idx_begin); | |
std::copy(std::begin(token), std::end(token), itBegin); | |
return std::forward<Container>(xs); | |
} | |
template <typename ContainerToken, typename Container> | |
Container set_segment(internal::create_new_container_t, | |
std::size_t idx_begin, const ContainerToken& token, const Container& xs) | |
{ | |
Container result = xs; | |
return set_segment(internal::reuse_container_t(), | |
idx_begin, token, std::move(result)); | |
} | |
} // namespace internal | |
// API search type: set_segment : (Int, [a], [a]) -> [a] | |
// fwd bind count: 2 | |
// Replace part of a sequence with a token. | |
// set_segment(2, [9,9,9], [0,1,2,3,4,5,6,7,8]) == [0,1,9,9,9,5,6,7,8] | |
// Crashes on invalid indices. | |
// Also known as replace_segment. | |
template <typename ContainerToken, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut set_segment(std::size_t idx_begin, const ContainerToken& token, Container&& xs) | |
{ | |
return internal::set_segment(internal::can_reuse_v<Container> {}, | |
idx_begin, token, std::forward<Container>(xs)); | |
} | |
namespace internal { | |
template <typename Container> | |
Container remove_segment(internal::reuse_container_t, | |
std::size_t idx_begin, std::size_t idx_end, Container&& xs) | |
{ | |
assert(idx_begin <= idx_end); | |
assert(idx_end <= size_of_cont(xs)); | |
auto firstBreakIt = std::begin(xs); | |
internal::advance_iterator(firstBreakIt, idx_begin); | |
auto secondBreakIt = std::begin(xs); | |
internal::advance_iterator(secondBreakIt, idx_end); | |
xs.erase( | |
std::copy(secondBreakIt, std::end(xs), firstBreakIt), std::end(xs)); | |
return std::forward<Container>(xs); | |
} | |
template <typename Container> | |
Container remove_segment(internal::create_new_container_t, | |
std::size_t idx_begin, std::size_t idx_end, const Container& xs) | |
{ | |
assert(idx_begin <= idx_end); | |
assert(idx_end <= size_of_cont(xs)); | |
Container result; | |
std::size_t length = idx_end - idx_begin; | |
internal::prepare_container(result, size_of_cont(xs) - length); | |
auto firstBreakIt = std::begin(xs); | |
internal::advance_iterator(firstBreakIt, idx_begin); | |
std::copy(std::begin(xs), firstBreakIt, internal::get_back_inserter(result)); | |
auto secondBreakIt = std::begin(xs); | |
internal::advance_iterator(secondBreakIt, idx_end); | |
std::copy(secondBreakIt, std::end(xs), internal::get_back_inserter(result)); | |
return result; | |
} | |
} // namespace internal | |
// API search type: remove_segment : (Int, Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Cuts our a defined segment from the sequence. | |
// remove_segment(2, 5, [0,1,2,3,4,5,6,7]) == [0,1,5,6,7] | |
// crashes on invalid indices | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut remove_segment( | |
std::size_t idx_begin, std::size_t idx_end, Container&& xs) | |
{ | |
return internal::remove_segment(internal::can_reuse_v<Container> {}, | |
idx_begin, idx_end, std::forward<Container>(xs)); | |
} | |
// API search type: insert_at : (Int, [a], [a]) -> [a] | |
// fwd bind count: 2 | |
// Inserts a token into a sequence at a specific position. | |
// insert_at(2, [8,9], [0,1,2,3,4]) == [0,1,8,9,2,3,4] | |
// Unsafe! Crashes on invalid index. | |
template <typename Container> | |
Container insert_at(std::size_t idx_begin, | |
const Container& token, const Container& xs) | |
{ | |
assert(idx_begin <= size_of_cont(xs)); | |
Container result; | |
internal::prepare_container(result, size_of_cont(xs) + size_of_cont(token)); | |
auto breakIt = std::begin(xs); | |
internal::advance_iterator(breakIt, idx_begin); | |
std::copy(std::begin(xs), breakIt, internal::get_back_inserter(result)); | |
std::copy(std::begin(token), std::end(token), internal::get_back_inserter(result)); | |
std::copy(breakIt, std::end(xs), internal::get_back_inserter(result)); | |
return result; | |
} | |
// API search type: elem_at_idx : (Int, [a]) -> a | |
// fwd bind count: 1 | |
// Return the nth element of a sequence. | |
// elem_at_idx(2, [7,6,5,4,3]) == 5 | |
// Unsafe! Crashes on invalid index. | |
template <typename Container> | |
auto elem_at_idx(std::size_t idx, const Container& xs) | |
{ | |
assert(idx < size_of_cont(xs)); | |
auto it = std::begin(xs); | |
internal::advance_iterator(it, idx); | |
return *it; | |
} | |
// API search type: elem_at_idx_maybe : (Int, [a]) -> Maybe a | |
// fwd bind count: 1 | |
// Return the nth element of a sequence if existing. | |
// elem_at_idx_maybe(2, [7,6,5,4,3]) == Just 5 | |
// elem_at_idx_maybe(9, [7,6,5,4,3]) == Nothing | |
// Use elem_at_idx_or_nothing if you want to provide a signed index type. | |
template <typename Container, | |
typename T = typename Container::value_type> | |
maybe<T> elem_at_idx_maybe(std::size_t idx, const Container& xs) | |
{ | |
if (size_of_cont(xs) < idx) { | |
return {}; | |
} | |
auto it = std::begin(xs); | |
internal::advance_iterator(it, idx); | |
return *it; | |
} | |
// API search type: elems_at_idxs : ([Int], [a]) -> [a] | |
// fwd bind count: 1 | |
// Construct a subsequence from the elements with the given indices. | |
// elem_at_idxs([1, 3], [7,6,5,4,3]) == [6, 4] | |
template <typename Container, | |
typename ContainerIdxs, | |
typename T = typename Container::value_type, | |
typename ContainerOut = std::vector<T>> | |
std::vector<T> elems_at_idxs(const ContainerIdxs& idxs, const Container& xs) | |
{ | |
static_assert(std::is_same<typename ContainerIdxs::value_type, std::size_t>::value, | |
"Indices must be std::size_t"); | |
ContainerOut result; | |
internal::prepare_container(result, size_of_cont(idxs)); | |
auto itOut = internal::get_back_inserter(result); | |
for (std::size_t idx : idxs) { | |
*itOut = elem_at_idx(idx, xs); | |
} | |
return result; | |
} | |
namespace internal { | |
template <typename Container, typename F> | |
Container transform(internal::reuse_container_t, F f, Container&& xs) | |
{ | |
internal::trigger_static_asserts<internal::unary_function_tag, | |
F, | |
decltype(*std::begin(xs))>(); | |
std::transform(std::begin(xs), std::end(xs), std::begin(xs), f); | |
return std::forward<Container>(xs); | |
} | |
template <typename ContainerOut, typename F, typename ContainerIn> | |
ContainerOut transform(internal::create_new_container_t, F f, | |
const ContainerIn& xs) | |
{ | |
internal::trigger_static_asserts<internal::unary_function_tag, | |
F, | |
decltype(*std::begin(xs))>(); | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it = internal::get_back_inserter<ContainerOut>(ys); | |
std::transform(std::begin(xs), std::end(xs), it, f); | |
return ys; | |
} | |
} // namespace internal | |
// API search type: transform : ((a -> b), [a]) -> [b] | |
// fwd bind count: 1 | |
// Apply a function to every element in a sequence. | |
// transform((*2), [1, 3, 4]) == [2, 6, 8] | |
// Also known as map or fmap. | |
template <typename F, typename ContainerIn, | |
typename ContainerOut = typename internal::same_cont_new_t_from_unary_f< | |
internal::remove_const_and_ref_t<ContainerIn>, F, 0>::type> | |
ContainerOut transform(F f, ContainerIn&& xs) | |
{ | |
using reuse_t = typename std::conditional< | |
std::is_same< | |
internal::can_reuse_v<ContainerIn>, | |
internal::reuse_container_t>::value | |
&& std::is_base_of< | |
std::true_type, | |
internal::has_order<ContainerIn>>::value | |
&& std::is_same< | |
internal::remove_const_and_ref_t<ContainerIn>, | |
ContainerOut>::value, | |
internal::reuse_container_t, | |
internal::create_new_container_t>::type; | |
return internal::transform<ContainerOut>( | |
reuse_t {}, f, std::forward<ContainerIn>(xs)); | |
} | |
// API search type: transform_convert : ((a -> b), [a]) -> [b] | |
// fwd bind count: 1 | |
// transform_convert((*2), [1, 3, 4]) == [2, 6, 8] | |
// Same as transform, but makes it easy to | |
// use an output container type different from the input container type. | |
template <typename ContainerOut, typename F, typename ContainerIn> | |
ContainerOut transform_convert(F f, const ContainerIn& xs) | |
{ | |
internal::trigger_static_asserts<internal::unary_function_tag, F, typename ContainerIn::value_type>(); | |
ContainerOut ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it = internal::get_back_inserter<ContainerOut>(ys); | |
std::transform(std::begin(xs), std::end(xs), it, f); | |
return ys; | |
} | |
// API search type: transform_inner : ((a -> b), [[a]]) -> [[b]] | |
// fwd bind count: 1 | |
// Applies a function to the elements of the inner containers | |
// of a nested sequence. | |
// transform_inner((*2), [[1, 3, 4], [1, 2]]) == [[2, 6, 8], [2, 4]] | |
// Also known as transform_nested, map_nested or map_inner. | |
template <typename F, typename ContainerIn, | |
typename ContainerOut = | |
typename internal::same_cont_new_t< | |
ContainerIn, | |
typename internal::same_cont_new_t_from_unary_f< | |
typename ContainerIn::value_type, F, 0>::type, | |
0>::type> | |
ContainerOut transform_inner(F f, const ContainerIn& xs) | |
{ | |
internal::trigger_static_asserts<internal::unary_function_tag, F, typename ContainerIn::value_type::value_type>(); | |
return fplus::transform( | |
fplus::bind_1st_of_2( | |
fplus::transform<F, const typename ContainerIn::value_type&>, f), | |
xs); | |
} | |
namespace internal { | |
template <typename Container> | |
Container reverse(internal::reuse_container_t, Container&& xs) | |
{ | |
static_assert(internal::has_order<Container>::value, | |
"Reverse: Container has no order."); | |
std::reverse(std::begin(xs), std::end(xs)); | |
return std::forward<Container>(xs); | |
} | |
template <typename Container> | |
Container reverse(internal::create_new_container_t, const Container& xs) | |
{ | |
static_assert(internal::has_order<Container>::value, | |
"Reverse: Container has no order."); | |
Container ys = xs; | |
std::reverse(std::begin(ys), std::end(ys)); | |
return ys; | |
} | |
} // namespace internal | |
// API search type: reverse : [a] -> [a] | |
// fwd bind count: 0 | |
// Reverse a sequence. | |
// reverse([0,4,2,6]) == [6,2,4,0] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut reverse(Container&& xs) | |
{ | |
return internal::reverse(internal::can_reuse_v<Container> {}, | |
std::forward<Container>(xs)); | |
} | |
// API search type: take : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Return the first n elements of a sequence xs. | |
// In case n >= length(xs), xs is returned. | |
// take(3, [0,1,2,3,4,5,6,7]) == [0,1,2] | |
// take(10, [0,1,2]) == [0,1,2] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut take(std::size_t amount, Container&& xs) | |
{ | |
if (amount >= size_of_cont(xs)) | |
return xs; | |
return get_segment(0, amount, std::forward<Container>(xs)); | |
} | |
// API search type: take_exact : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Return exactly the first n elements of a sequence xs. | |
// Unsafe! Crashes then sequence is too short. | |
// take_exact(3, [0,1,2,3,4,5,6,7]) == [0,1,2] | |
// take_exact(10, [0,1,2]) == crash | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut take_exact(std::size_t amount, Container&& xs) | |
{ | |
return get_segment(0, amount, std::forward<Container>(xs)); | |
} | |
// API search type: take_cyclic : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Takes n elements from a sequence considering it as cyclic. | |
// take_cyclic(5, [0,1,2,3]) == [0,1,2,3,0] | |
// take_cyclic(7, [0,1,2,3]) == [0,1,2,3,0,1,2] | |
// take_cyclic(7, [0,1]) == [0,1,0,1,0,1,0] | |
// take_cyclic(2, [0,1,2,3]) == [0,1] | |
// take_cyclic(3, [0]) == [0,0,0] | |
// take_cyclic(3, []) == crash! | |
// Also known as take_wrap. | |
// xs must be non-empty. | |
template <typename Container> | |
Container take_cyclic(std::size_t amount, const Container& xs) | |
{ | |
assert(!xs.empty()); | |
Container ys; | |
internal::prepare_container(ys, size_of_cont(xs)); | |
auto it_out = internal::get_back_inserter(ys); | |
auto it_in = std::begin(xs); | |
while (amount != 0) { | |
*it_out = *it_in; | |
--amount; | |
++it_in; | |
if (it_in == std::end(xs)) { | |
it_in = std::begin(xs); | |
} | |
} | |
return ys; | |
} | |
// API search type: drop : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Skip the first n elements of a sequence xs. | |
// If n > length(xs) an empty sequence is returned. | |
// drop(3, [0,1,2,3,4,5,6,7]) == [3,4,5,6,7] | |
// Also known as skip. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut drop(std::size_t amount, Container&& xs) | |
{ | |
if (amount >= size_of_cont(xs)) | |
return ContainerOut(); | |
return get_segment(amount, size_of_cont(xs), std::forward<Container>(xs)); | |
} | |
// API search type: take_last : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Return the last n elements of a sequence xs. | |
// In case n >= length(xs), xs is returned. | |
// take_last(3, [0,1,2,3,4,5,6,7]) == [5,6,7] | |
// take_last(10, [0,1,2]) == [0,1,2] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut take_last(std::size_t amount, Container&& xs) | |
{ | |
if (amount >= size_of_cont(xs)) | |
return xs; | |
return drop(size_of_cont(xs) - amount, std::forward<Container>(xs)); | |
} | |
// API search type: drop_last : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Skip the last n elements of a sequence xs. | |
// If n > length(xs) an empty sequence is returned. | |
// drop_last(3, [0,1,2,3,4,5,6,7]) == [0,1,2,3,4] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut drop_last(std::size_t amount, Container&& xs) | |
{ | |
if (amount >= size_of_cont(xs)) | |
return ContainerOut(); | |
return take(size_of_cont(xs) - amount, std::forward<Container>(xs)); | |
} | |
// API search type: drop_exact : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Skip exactly the first n elements of a sequence xs. | |
// Unsafe! Crashes when xs is too short. | |
// drop_exact(3, [0,1,2,3,4,5,6,7]) == [3,4,5,6,7] | |
// drop_exact(10, [0,1,2,3,4,5,6,7]) == crash | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut drop_exact(std::size_t amount, Container&& xs) | |
{ | |
return get_segment(amount, size_of_cont(xs), std::forward<Container>(xs)); | |
} | |
// API search type: take_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Take elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// take_while(is_even, [0,2,4,5,6,7,8]) == [0,2,4] | |
template <typename Container, typename UnaryPredicate> | |
Container take_while(UnaryPredicate pred, const Container& xs) | |
{ | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
auto itFirst = std::find_if( | |
std::begin(xs), std::end(xs), logical_not(pred)); | |
if (itFirst == std::end(xs)) | |
return xs; | |
return Container(std::begin(xs), itFirst); | |
} | |
// API search type: take_last_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Take elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// take_last_while(is_even, [0,2,7,5,6,4,8]) == [6,4,8] | |
template <typename Container, typename UnaryPredicate> | |
Container take_last_while(UnaryPredicate pred, const Container& xs) | |
{ | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
const auto r_begin = internal::make_reverse_iterator(std::end(xs)); | |
const auto r_end = internal::make_reverse_iterator(std::begin(xs)); | |
const auto itFirstReverse = std::find_if(r_begin, r_end, logical_not(pred)); | |
if (itFirstReverse == r_begin) | |
return Container(); | |
if (itFirstReverse == r_end) | |
return xs; | |
return Container(itFirstReverse.base(), std::end(xs)); | |
} | |
// API search type: drop_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Remove elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// drop_while(is_even, [0,2,4,5,6,7,8]) == [5,6,7,8] | |
// Also known as trim_left_by. | |
template <typename Container, typename UnaryPredicate> | |
Container drop_while(UnaryPredicate pred, const Container& xs) | |
{ | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
auto itFirstNot = std::find_if_not(std::begin(xs), std::end(xs), pred); | |
if (itFirstNot == std::end(xs)) | |
return Container(); | |
return Container(itFirstNot, std::end(xs)); | |
} | |
// API search type: drop_last_while : ((a -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Remove elements from the beginning of a sequence | |
// as long as they are fulfilling a predicate. | |
// drop_last_while(is_even, [0,2,7,5,6,4,8]) == [0,2,7,5] | |
template <typename Container, typename UnaryPredicate> | |
Container drop_last_while(UnaryPredicate pred, const Container& xs) | |
{ | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
const auto r_begin = internal::make_reverse_iterator(std::end(xs)); | |
const auto r_end = internal::make_reverse_iterator(std::begin(xs)); | |
const auto itFirstNotReverse = std::find_if_not(r_begin, r_end, pred); | |
if (itFirstNotReverse == r_begin) | |
return xs; | |
if (itFirstNotReverse == r_end) | |
return Container(); | |
return Container(std::begin(xs), itFirstNotReverse.base()); | |
} | |
// API search type: fold_left : (((a, b) -> a), a, [b]) -> a | |
// fwd bind count: 2 | |
// fold_left((+), 0, [1, 2, 3]) == ((0+1)+2)+3 == 6 | |
// Takes the second argument and the first item of the list | |
// and applies the function to them, | |
// then feeds the function with this result and the second argument and so on. | |
template <typename F, typename Container, typename Acc> | |
Acc fold_left(F f, const Acc& init, const Container& xs) | |
{ | |
using std::begin; | |
using std::end; | |
return internal::accumulate(begin(xs), end(xs), init, f); | |
} | |
// API search type: reduce : (((a, a) -> a), a, [a]) -> a | |
// fwd bind count: 2 | |
// reduce((+), 0, [1, 2, 3]) == (0+1+2+3) == 6 | |
// Combines the initial value and all elements of the sequence | |
// using the given function. | |
// The set of f, init and value_type should form a monoid. | |
template <typename F, typename Container> | |
typename Container::value_type reduce( | |
F f, const typename Container::value_type& init, const Container& xs) | |
{ | |
return fold_left(f, init, xs); | |
} | |
// API search type: fold_left_1 : (((a, a) -> a), [a]) -> a | |
// fwd bind count: 1 | |
// fold_left_1((+), [1, 2, 3]) == (1+2)+3 == 6 | |
// Takes the first 2 items of the list and applies the function to them, | |
// then feeds the function with this result and the third argument and so on. | |
// xs must be non-empty. | |
template <typename F, typename Container> | |
auto fold_left_1(F f, const Container& xs) | |
{ | |
assert(!xs.empty()); | |
using std::begin; | |
using std::end; | |
const auto it = begin(xs); | |
return internal::accumulate(std::next(it), end(xs), *it, f); | |
} | |
// API search type: reduce_1 : (((a, a) -> a), [a]) -> a | |
// fwd bind count: 1 | |
// reduce_1((+), [1, 2, 3]) == (1+2+3) == 6 | |
// Joins all elements of the sequence using the given function. | |
// The set of f and value_type should form a semigroup. | |
template <typename F, typename Container> | |
typename Container::value_type reduce_1(F f, const Container& xs) | |
{ | |
assert(!xs.empty()); | |
return fold_left_1(f, xs); | |
} | |
// API search type: fold_right : (((a, b) -> b), b) -> [a] -> b | |
// fwd bind count: 2 | |
// fold_right((+), 0, [1, 2, 3]) == 1+(2+(3+0)) == 6 | |
// Takes the second argument and the last item of the list | |
// and applies the function, | |
// then it takes the penultimate item from the end and the result, and so on. | |
template <typename F, typename Container, typename Acc> | |
Acc fold_right(F f, const Acc& init, const Container& xs) | |
{ | |
return internal::accumulate(xs.rbegin(), xs.rend(), init, flip(f)); | |
} | |
// API search type: fold_right_1 : (((a, a) -> a), [a]) -> a | |
// fwd bind count: 1 | |
// fold_right_1((+), [1, 2, 3]) == 1+(2+3)) == 6 | |
// Takes the last two items of the list and applies the function, | |
// then it takes the third item from the end and the result, and so on. | |
template <typename F, typename Container> | |
auto fold_right_1(F f, const Container& xs) | |
{ | |
assert(!xs.empty()); | |
const auto it = xs.rbegin(); | |
return internal::accumulate(std::next(it), xs.rend(), *it, flip(f)); | |
} | |
// API search type: scan_left : (((a, b) -> a), a, [b]) -> [a] | |
// fwd bind count: 2 | |
// scan_left((+), 0, [1, 2, 3]) == [0, 1, 3, 6] | |
// Takes the second argument and the first item of the list | |
// and applies the function to them, | |
// then feeds the function with this result and the second argument and so on. | |
// It returns the list of intermediate and final results. | |
template <typename F, typename ContainerIn, typename Acc> | |
auto scan_left(F f, const Acc& init, const ContainerIn& xs) | |
{ | |
using ContainerOut = | |
typename internal::same_cont_new_t<ContainerIn, Acc, 1>::type; | |
ContainerOut result; | |
internal::prepare_container(result, size_of_cont(xs) + 1); | |
using std::begin; | |
using std::end; | |
internal::scan_impl( | |
f, init, internal::get_back_inserter(result), begin(xs), end(xs)); | |
return result; | |
} | |
// API search type: scan_left_1 : (((a, a) -> a), [a]) -> [a] | |
// fwd bind count: 1 | |
// scan_left_1((+), [1, 2, 3]) == [1, 3, 6] | |
// Takes the first 2 items of the list and applies the function to them, | |
// then feeds the function with this result and the third argument and so on. | |
// It returns the list of intermediate and final results. | |
// xs must be non-empty. | |
template <typename F, typename ContainerIn> | |
auto scan_left_1(F f, const ContainerIn& xs) | |
{ | |
assert(!xs.empty()); | |
using std::begin; | |
using std::end; | |
const auto beginIt = begin(xs); | |
using ContainerOut = typename internal::same_cont_new_t< | |
ContainerIn, | |
internal::uncvref_t<decltype(*beginIt)>, | |
0>::type; | |
ContainerOut result; | |
internal::prepare_container(result, size_of_cont(xs)); | |
internal::scan_impl(f, | |
*beginIt, | |
internal::get_back_inserter(result), | |
std::next(beginIt), | |
end(xs)); | |
return result; | |
} | |
// API search type: scan_right : (((a, b) -> b), b, [a]) -> [b] | |
// fwd bind count: 2 | |
// scan_right((+), 0, [1, 2, 3]) == [6, 5, 3, 0] | |
// Takes the second argument and the last item of the list | |
// and applies the function, | |
// then it takes the penultimate item from the end and the result, and so on. | |
// It returns the list of intermediate and final results. | |
template <typename F, typename ContainerIn, | |
typename Acc = typename utils::function_traits<F>::template arg<1>::type, | |
typename ContainerOut = typename internal::same_cont_new_t<ContainerIn, Acc, 1>::type> | |
ContainerOut scan_right(F f, const Acc& init, const ContainerIn& xs) | |
{ | |
return reverse(scan_left(flip(f), init, reverse(xs))); | |
} | |
// API search type: scan_right_1 : (((a, a) -> a), [a]) -> [a] | |
// fwd bind count: 1 | |
// scan_right_1((+), [1, 2, 3]) == [6, 5, 3] | |
// Takes the last two items of the list and applies the function, | |
// then it takes the third item from the end and the result, and so on. | |
// It returns the list of inntermediate and final results. | |
template <typename F, typename ContainerIn, | |
typename Acc = typename ContainerIn::value_type, | |
typename ContainerOut = typename internal::same_cont_new_t<ContainerIn, Acc, 0>::type> | |
ContainerOut scan_right_1(F f, const ContainerIn& xs) | |
{ | |
return reverse(scan_left_1(flip(f), reverse(xs))); | |
} | |
// API search type: sum : [a] -> a | |
// fwd bind count: 0 | |
// Adds up all values in a sequence. | |
// sum([0,3,1]) == 4 | |
// sum([]) == 0 | |
template <typename Container, | |
typename T = typename Container::value_type> | |
T sum(const Container& xs) | |
{ | |
T result = T(); | |
for (const auto& x : xs) { | |
result = result + x; | |
} | |
return result; | |
} | |
// API search type: product : [a] -> a | |
// fwd bind count: 0 | |
// Returns the product of all values in a sequence. | |
// product([3,1,2]) == 6 | |
// product([]) == 1 | |
template <typename Container, | |
typename T = typename Container::value_type> | |
T product(const Container& xs) | |
{ | |
T result { 1 }; | |
for (const auto& x : xs) { | |
result = result * x; | |
} | |
return result; | |
} | |
namespace internal { | |
template <typename T, typename Container> | |
Container append_elem(internal::reuse_container_t, const T& y, Container&& xs) | |
{ | |
*internal::get_back_inserter(xs) = y; | |
return std::forward<Container>(xs); | |
} | |
template <typename T, typename Container> | |
Container append_elem(internal::create_new_container_t, const T& y, | |
const Container& xs) | |
{ | |
Container result; | |
internal::prepare_container(result, size_of_cont(xs) + 1); | |
std::copy(std::begin(xs), std::end(xs), | |
internal::get_back_inserter(result)); | |
*internal::get_back_inserter(result) = y; | |
return result; | |
} | |
} // namespace internal | |
// API search type: append_elem : (a, [a]) -> [a] | |
// fwd bind count: 1 | |
// Extends a sequence with one element at the back. | |
// append_elem([1, 2], 3) == [1, 2, 3] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>, | |
typename T = typename ContainerOut::value_type> | |
ContainerOut append_elem(const T& y, Container&& xs) | |
{ | |
return internal::append_elem(internal::can_reuse_v<Container> {}, | |
y, std::forward<Container>(xs)); | |
} | |
namespace internal { | |
template <typename T> | |
std::list<T> prepend_elem(internal::reuse_container_t, | |
const T& y, std::list<T>&& xs) | |
{ | |
xs.push_front(y); | |
return std::forward<std::list<T>>(xs); | |
} | |
template <typename T, typename Container> | |
Container prepend_elem(internal::reuse_container_t, | |
const T& y, Container&& xs) | |
{ | |
xs.resize(size_of_cont(xs) + 1); | |
std::copy(++xs.rbegin(), xs.rend(), xs.rbegin()); | |
*std::begin(xs) = y; | |
return std::forward<Container>(xs); | |
} | |
template <typename T, typename Container> | |
Container prepend_elem(internal::create_new_container_t, const T& y, | |
const Container& xs) | |
{ | |
Container result; | |
internal::prepare_container(result, size_of_cont(xs) + 1); | |
*internal::get_back_inserter(result) = y; | |
std::copy(std::begin(xs), std::end(xs), | |
internal::get_back_inserter(result)); | |
return result; | |
} | |
} // namespace internal | |
// API search type: prepend_elem : (a, [a]) -> [a] | |
// fwd bind count: 1 | |
// Extends a sequence with one element in the front. | |
// prepend_elem([2, 3], 1) == [1, 2, 3] | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>, | |
typename T = typename ContainerOut::value_type> | |
ContainerOut prepend_elem(const T& y, Container&& xs) | |
{ | |
return internal::prepend_elem(internal::can_reuse_v<Container> {}, | |
y, std::forward<Container>(xs)); | |
} | |
// API search type: append : ([a], [a]) -> [a] | |
// fwd bind count: 1 | |
// Concatenates two sequences. | |
// append([1, 2], [3, 4, 5]) == [1, 2, 3, 4, 5] | |
template <typename ContainerIn1, typename ContainerIn2 = ContainerIn1, typename ContainerOut = ContainerIn1> | |
ContainerOut append(const ContainerIn1& xs, const ContainerIn2& ys) | |
{ | |
ContainerOut result; | |
internal::prepare_container(result, size_of_cont(xs) + size_of_cont(ys)); | |
std::copy(std::begin(xs), std::end(xs), | |
internal::get_back_inserter(result)); | |
std::copy(std::begin(ys), std::end(ys), | |
internal::get_back_inserter(result)); | |
return result; | |
} | |
// API search type: append_convert : ([a], [a]) -> [a] | |
// fwd bind count: 1 | |
// Same as append, but makes it easier to | |
// use an output container type different from the input container type. | |
template <typename ContainerOut, typename ContainerIn1, typename ContainerIn2 = ContainerIn1> | |
ContainerOut append_convert(const ContainerIn1& xs, const ContainerIn2& ys) | |
{ | |
return append<ContainerIn1, ContainerIn2, ContainerOut>(xs, ys); | |
} | |
// API search type: concat : [[a]] -> [a] | |
// fwd bind count: 0 | |
// Concatenates multiple sequences. | |
// concat([[1, 2], [], [3]]) == [1, 2, 3] | |
// Also known as flatten. | |
template <typename ContainerIn, | |
typename ContainerOut = typename ContainerIn::value_type> | |
ContainerOut concat(const ContainerIn& xss) | |
{ | |
std::size_t length = sum( | |
transform(size_of_cont<typename ContainerIn::value_type>, xss)); | |
ContainerOut result; | |
internal::prepare_container(result, length); | |
using std::begin; | |
using std::end; | |
for (const auto& xs : xss) { | |
result.insert(end(result), begin(xs), end(xs)); | |
} | |
return result; | |
} | |
// API search type: interweave : ([a], [a]) -> [a] | |
// fwd bind count: 1 | |
// Return a sequence that contains elements from the two provided sequences | |
// in alternating order. If one list runs out of items, | |
// appends the items from the remaining list. | |
// interweave([1,3], [2,4]) == [1,2,3,4] | |
// interweave([1,3,5,7], [2,4]) == [1,2,3,4,5,7] | |
// See interleave for interweaving more than two sequences. | |
template <typename Container> | |
Container interweave(const Container& xs, const Container& ys) | |
{ | |
Container result; | |
internal::prepare_container(result, size_of_cont(xs) + size_of_cont(ys)); | |
auto it = internal::get_back_inserter<Container>(result); | |
auto it_xs = std::begin(xs); | |
auto it_ys = std::begin(ys); | |
while (it_xs != std::end(xs) || it_ys != std::end(ys)) { | |
if (it_xs != std::end(xs)) | |
*it = *(it_xs++); | |
if (it_ys != std::end(ys)) | |
*it = *(it_ys++); | |
} | |
return result; | |
} | |
// API search type: unweave : [a] -> ([a], [a]) | |
// fwd bind count: 0 | |
// Puts the elements with an even index into the first list, | |
// and the elements with an odd index into the second list. | |
// Inverse of interweave. | |
// unweave([0,1,2,3]) == ([0,2], [1,3]) | |
// unweave([0,1,2,3,4]) == ([0,2,4], [1,3]) | |
template <typename Container> | |
std::pair<Container, Container> unweave(const Container& xs) | |
{ | |
std::pair<Container, Container> result; | |
if (is_even(size_of_cont(xs))) | |
internal::prepare_container(result.first, size_of_cont(xs) / 2); | |
else | |
internal::prepare_container(result.first, size_of_cont(xs) / 2 + 1); | |
internal::prepare_container(result.second, size_of_cont(xs) / 2); | |
auto it_even = internal::get_back_inserter<Container>(result.first); | |
auto it_odd = internal::get_back_inserter<Container>(result.second); | |
std::size_t counter = 0; | |
for (const auto& x : xs) { | |
if (counter++ % 2 == 0) | |
*it_even = x; | |
else | |
*it_odd = x; | |
} | |
return result; | |
} | |
namespace internal { | |
template <typename Compare, typename T> | |
std::list<T> sort_by(internal::reuse_container_t, Compare comp, | |
std::list<T>&& xs) | |
{ | |
xs.sort(comp); | |
return std::forward<std::list<T>>(xs); | |
} | |
template <typename Compare, typename T> | |
std::list<T> sort_by(internal::create_new_container_t, Compare comp, | |
const std::list<T>& xs) | |
{ | |
auto result = xs; | |
result.sort(comp); | |
return result; | |
} | |
template <typename Compare, typename Container> | |
Container sort_by(internal::reuse_container_t, Compare comp, Container&& xs) | |
{ | |
std::sort(std::begin(xs), std::end(xs), comp); | |
return std::forward<Container>(xs); | |
} | |
template <typename Compare, typename Container> | |
Container sort_by(internal::create_new_container_t, Compare comp, | |
const Container& xs) | |
{ | |
auto result = xs; | |
std::sort(std::begin(result), std::end(result), comp); | |
return result; | |
} | |
} // namespace internal | |
// API search type: sort_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence by given less comparator. | |
template <typename Compare, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut sort_by(Compare comp, Container&& xs) | |
{ | |
return internal::sort_by(internal::can_reuse_v<Container> {}, | |
comp, std::forward<Container>(xs)); | |
} | |
namespace internal { | |
// workarounds for clang bug 24115 | |
// (std::sort and std::unique with std::function as comp) | |
// https://llvm.org/bugs/show_bug.cgi?id=24115 | |
template <typename F> | |
struct is_less_by_struct { | |
is_less_by_struct(F f) | |
: f_(f) {}; | |
template <typename T> | |
bool operator()(const T& x, const T& y) | |
{ | |
return f_(x) < f_(y); | |
} | |
private: | |
F f_; | |
}; | |
template <typename F> | |
struct is_equal_by_struct { | |
is_equal_by_struct(F f) | |
: f_(f) {}; | |
template <typename T> | |
bool operator()(const T& x, const T& y) | |
{ | |
return f_(x) == f_(y); | |
} | |
private: | |
F f_; | |
}; | |
} | |
// API search type: sort_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence by a given transformer. | |
template <typename F, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut sort_on(F f, Container&& xs) | |
{ | |
return sort_by(internal::is_less_by_struct<F>(f), | |
std::forward<Container>(xs)); | |
} | |
// API search type: sort : [a] -> [a] | |
// fwd bind count: 0 | |
// Sort a sequence to ascending order using std::less. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut sort(Container&& xs) | |
{ | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return sort_by(std::less<T>(), std::forward<Container>(xs)); | |
} | |
namespace internal { | |
template <typename Compare, typename T> | |
std::list<T> stable_sort_by(internal::reuse_container_t, Compare comp, | |
std::list<T>&& xs) | |
{ | |
xs.sort(comp); // std::list<T>::sort ist already stable. | |
return std::forward<std::list<T>>(xs); | |
} | |
template <typename Compare, typename T> | |
std::list<T> stable_sort_by(internal::create_new_container_t, Compare comp, | |
const std::list<T>& xs) | |
{ | |
auto result = xs; | |
result.sort(comp); // std::list<T>::sort ist already stable. | |
return result; | |
} | |
template <typename Compare, typename Container> | |
Container stable_sort_by(internal::reuse_container_t, Compare comp, | |
Container&& xs) | |
{ | |
std::sort(std::begin(xs), std::end(xs), comp); | |
return std::forward<Container>(xs); | |
} | |
template <typename Compare, typename Container> | |
Container stable_sort_by(internal::create_new_container_t, Compare comp, | |
const Container& xs) | |
{ | |
auto result = xs; | |
std::sort(std::begin(result), std::end(result), comp); | |
return result; | |
} | |
} // namespace internal | |
// API search type: stable_sort_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence stably by given less comparator. | |
template <typename Compare, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut stable_sort_by(Compare comp, Container&& xs) | |
{ | |
return internal::stable_sort_by(internal::can_reuse_v<Container> {}, | |
comp, std::forward<Container>(xs)); | |
} | |
// API search type: stable_sort_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Sort a sequence stably by given transformer. | |
template <typename F, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut stable_sort_on(F f, Container&& xs) | |
{ | |
return stable_sort_by(internal::is_less_by_struct<F>(f), | |
std::forward<Container>(xs)); | |
} | |
// API search type: stable_sort : [a] -> [a] | |
// fwd bind count: 0 | |
// Sort a sequence stably to ascending order using std::less. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut stable_sort(Container&& xs) | |
{ | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return stable_sort_by(std::less<T>(), std::forward<Container>(xs)); | |
} | |
namespace internal { | |
template <typename Compare, typename Container> | |
Container partial_sort_by(internal::reuse_container_t, Compare comp, | |
std::size_t count, Container&& xs) | |
{ | |
if (count > xs.size()) { | |
count = xs.size(); | |
} | |
auto middle = std::begin(xs); | |
internal::advance_iterator(middle, count); | |
std::partial_sort(std::begin(xs), middle, std::end(xs), comp); | |
return std::forward<Container>(get_segment(internal::reuse_container_t(), | |
0, count, xs)); | |
} | |
template <typename Compare, typename Container> | |
Container partial_sort_by(internal::create_new_container_t, Compare comp, | |
std::size_t count, const Container& xs) | |
{ | |
auto result = xs; | |
return partial_sort_by( | |
internal::reuse_container_t(), comp, count, std::move(result)); | |
} | |
} // namespace internal | |
// API search type: partial_sort_by : (((a, a) -> Bool), Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Partially sort a sequence by a given less comparator. | |
// Returns only the sorted segment. | |
template <typename Compare, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut partial_sort_by(Compare comp, std::size_t count, Container&& xs) | |
{ | |
return internal::partial_sort_by(internal::can_reuse_v<Container> {}, | |
comp, count, std::forward<Container>(xs)); | |
} | |
// API search type: partial_sort_on : ((a -> b), Int, [a]) -> [a] | |
// fwd bind count: 2 | |
// Partially sort a sequence by a given transformer. | |
// Returns only the sorted segment. | |
template <typename F, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut partial_sort_on(F f, std::size_t count, Container&& xs) | |
{ | |
return partial_sort_by(internal::is_less_by_struct<F>(f), count, | |
std::forward<Container>(xs)); | |
} | |
// API search type: partial_sort : (Int, [a]) -> [a] | |
// fwd bind count: 1 | |
// Partially sort a sequence in ascending order using std::less. | |
// Returns only the sorted segment. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut partial_sort(std::size_t count, Container&& xs) | |
{ | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return partial_sort_by(std::less<T>(), count, | |
std::forward<Container>(xs)); | |
} | |
// API search type: nth_element_by : (((a, a) -> Bool), Int, [a]) -> a | |
// fwd bind count: 2 | |
// Return the nth largest element of a sequence by a given less comparator. | |
template <typename Compare, typename Container, | |
typename T = typename Container::value_type> | |
T nth_element_by(Compare comp, std::size_t n, const Container& xs) | |
{ | |
assert(n < xs.size()); | |
auto result = xs; | |
auto middle = std::begin(result); | |
internal::advance_iterator(middle, n); | |
std::nth_element(std::begin(result), middle, std::end(result), comp); | |
return *middle; | |
} | |
// API search type: nth_element_on : ((a -> b), Int, [a]) -> a | |
// fwd bind count: 2 | |
// Return the nth largest element of a sequence by a given transformer. | |
template <typename F, typename Container, | |
typename T = typename Container::value_type> | |
T nth_element_on(F f, std::size_t n, const Container& xs) | |
{ | |
return nth_element_by(internal::is_less_by_struct<F>(f), n, xs); | |
} | |
// API search type: nth_element : (Int, [a]) -> a | |
// fwd bind count: 1 | |
// Return the nth largest element of a sequence using std::less. | |
template <typename Container, | |
typename T = typename Container::value_type> | |
T nth_element(std::size_t n, const Container& xs) | |
{ | |
return nth_element_by(std::less<T>(), n, xs); | |
} | |
namespace internal { | |
template <typename BinaryPredicate, typename Container> | |
Container unique_by(internal::reuse_container_t, | |
BinaryPredicate pred, Container&& xs) | |
{ | |
internal::check_binary_predicate_for_container<BinaryPredicate, Container>(); | |
const auto it_end = std::unique(std::begin(xs), std::end(xs), pred); | |
xs.erase(it_end, std::end(xs)); | |
return std::forward<Container>(xs); | |
} | |
template <typename BinaryPredicate, typename Container> | |
Container unique_by(internal::create_new_container_t, | |
BinaryPredicate pred, const Container& xs) | |
{ | |
auto result = xs; | |
return unique_by(internal::reuse_container_t(), pred, std::move(result)); | |
} | |
} // namespace internal | |
// API search type: unique_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Like unique, eliminates all but the first element | |
// from every consecutive group of equivalent elements from the sequence; | |
// but with a user supplied equality predicate. | |
// See nub_by for making elements globally unique in a sequence. | |
// O(n) | |
template <typename BinaryPredicate, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut unique_by(BinaryPredicate pred, Container&& xs) | |
{ | |
return internal::unique_by(internal::can_reuse_v<Container> {}, | |
pred, std::forward<Container>(xs)); | |
} | |
// API search type: unique_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Like unique, eliminates all but the first element | |
// from every consecutive group of equivalent elements from the sequence; | |
// but with a user supplied transformation (e.g. getter). | |
// See nub_on for making elements globally unique in a sequence. | |
// O(n) | |
// Also known as drop_repeats. | |
template <typename F, typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut unique_on(F f, Container&& xs) | |
{ | |
return unique_by(internal::is_equal_by_struct<F>(f), | |
std::forward<Container>(xs)); | |
} | |
// API search type: unique : [a] -> [a] | |
// fwd bind count: 0 | |
// Eliminates all but the first element | |
// from every consecutive group of equivalent elements from the sequence. | |
// unique([1,2,2,3,2]) == [1,2,3,2] | |
// See nub for making elements globally unique in a sequence. | |
// O(n) | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut unique(Container&& xs) | |
{ | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return unique_on(identity<T>, std::forward<Container>(xs)); | |
} | |
// API search type: intersperse : (a, [a]) -> [a] | |
// fwd bind count: 1 | |
// Insert a value between all adjacent values in a sequence. | |
// intersperse(0, [1, 2, 3]) == [1, 0, 2, 0, 3] | |
// Also known as interpose. | |
template <typename Container, | |
typename X = typename Container::value_type> | |
Container intersperse(const X& value, const Container& xs) | |
{ | |
if (xs.empty()) | |
return Container(); | |
if (size_of_cont(xs) == 1) | |
return xs; | |
Container result; | |
internal::prepare_container(result, std::max<std::size_t>(0, size_of_cont(xs) * 2 - 1)); | |
auto it = internal::get_back_inserter(result); | |
for_each(std::begin(xs), --std::end(xs), [&value, &it](const X& x) { | |
*it = x; | |
*it = value; | |
}); | |
*it = xs.back(); | |
return result; | |
} | |
// API search type: join : ([a], [[a]]) -> [a] | |
// fwd bind count: 1 | |
// Inserts a separator sequence in between the elements | |
// of a sequence of sequences and concatenates the result. | |
// Also known as intercalate or implode. | |
// join(", ", ["a", "bee", "cee"]) == "a, bee, cee" | |
// join([0, 0], [[1], [2], [3, 4]]) == [1, 0, 0, 2, 0, 0, 3, 4] | |
template <typename Container, | |
typename X = typename Container::value_type> | |
X join(const X& separator, const Container& xs) | |
{ | |
return concat(intersperse(separator, xs)); | |
} | |
// API search type: join_elem : (a, [[a]]) -> [a] | |
// fwd bind count: 1 | |
// Inserts a separator in between the elements | |
// of a sequence of sequences and concatenates the result. | |
// Also known as intercalate_elem. | |
// join_elem(';', ["a", "bee", "cee"]) == "a;bee;cee" | |
// join_elem(0, [[1], [2], [3, 4]]) == [1, 0, 2, 0, 3, 4]] | |
template <typename Container, | |
typename Inner = typename Container::value_type, | |
typename X = typename Inner::value_type> | |
Inner join_elem(const X& separator, const Container& xs) | |
{ | |
return join(Inner(1, separator), xs); | |
} | |
// API search type: is_elem_of_by : ((a -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if at least one element of the container fulfils a predicate. | |
// is_elem_of_by((==), [1,2,3]) == true | |
template <typename UnaryPredicate, typename Container> | |
bool is_elem_of_by(UnaryPredicate pred, const Container& xs) | |
{ | |
return std::find_if(std::begin(xs), std::end(xs), pred) != std::end(xs); | |
} | |
// API search type: is_elem_of : (a, [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if an element is a member of a container. | |
// is_elem_of(2, [1,2,3]) == true | |
// Also known as flip(contains). | |
template <typename Container> | |
bool is_elem_of(const typename Container::value_type& x, const Container& xs) | |
{ | |
return is_elem_of_by(is_equal_to(x), xs); | |
} | |
// API search type: nub_by : (((a, a) -> Bool), [a]) -> [a] | |
// fwd bind count: 1 | |
// Makes the elements in a container unique with respect to a predicate | |
// nub_by((==), [1,2,2,3,2]) == [1,2,3] | |
// O(n^2) | |
template <typename Container, typename BinaryPredicate> | |
Container nub_by(BinaryPredicate p, const Container& xs) | |
{ | |
Container result; | |
auto itOut = internal::get_back_inserter(result); | |
for (const auto& x : xs) { | |
auto eqToX = bind_1st_of_2(p, x); | |
if (!is_elem_of_by(eqToX, result)) { | |
*itOut = x; | |
} | |
} | |
return result; | |
} | |
// API search type: nub_on : ((a -> b), [a]) -> [a] | |
// fwd bind count: 1 | |
// Makes the elements in a container unique | |
// with respect to their function value. | |
// nub_on((mod 10), [12,32,15]) == [12,15] | |
// O(n^2) | |
template <typename Container, typename F> | |
Container nub_on(F f, const Container& xs) | |
{ | |
return nub_by(is_equal_by(f), xs); | |
} | |
// API search type: nub : [a] -> [a] | |
// fwd bind count: 0 | |
// Makes the elements in a container unique. | |
// nub([1,2,2,3,2]) == [1,2,3] | |
// O(n^2) | |
// Also known as distinct. | |
template <typename Container> | |
Container nub(const Container& xs) | |
{ | |
typedef typename Container::value_type T; | |
return nub_by(std::equal_to<T>(), xs); | |
} | |
// API search type: all_unique_by_eq : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all elements in a container are unique | |
// with respect to a predicate. | |
// Returns true for empty containers. | |
// O(n^2) | |
template <typename Container, typename BinaryPredicate> | |
bool all_unique_by_eq(BinaryPredicate p, const Container& xs) | |
{ | |
internal::check_binary_predicate_for_container<BinaryPredicate, Container>(); | |
return size_of_cont(nub_by(p, xs)) == size_of_cont(xs); | |
} | |
// API search type: all_unique_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all elements in a container are unique | |
// with respect to their function values. | |
// Returns true for empty containers. | |
// O(n^2) | |
template <typename Container, typename F> | |
bool all_unique_on(F f, const Container& xs) | |
{ | |
return all_unique_by_eq(is_equal_by(f), xs); | |
} | |
// API search type: all_unique : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if all elements in a container are unique. | |
// Returns true for empty containers. | |
// O(n^2) | |
template <typename Container> | |
bool all_unique(const Container& xs) | |
{ | |
typedef typename Container::value_type T; | |
auto comp = std::equal_to<T>(); | |
return all_unique_by_eq(comp, xs); | |
} | |
// API search type: is_strictly_sorted_by : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is strictly sorted using a predicate. | |
// comp(a, b) must return true only if a < b. | |
// O(n) | |
template <typename Container, typename Compare> | |
bool is_strictly_sorted_by(Compare comp, const Container& xs) | |
{ | |
internal::check_compare_for_container<Compare, Container>(); | |
if (size_of_cont(xs) < 2) | |
return true; | |
auto it1 = std::begin(xs); | |
for (auto it2 = it1 + 1; it2 < std::end(xs); ++it1, ++it2) | |
if (!internal::invoke(comp, *it1, *it2)) | |
return false; | |
return true; | |
} | |
// API search type: is_strictly_sorted_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is strictly sorted using a transformer. | |
// O(n) | |
template <typename Container, typename F> | |
bool is_strictly_sorted_on(F f, const Container& xs) | |
{ | |
return is_strictly_sorted_by(is_less_by(f), xs); | |
} | |
// API search type: is_strictly_sorted : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if a container already is strictly sorted | |
// in ascending order using std::less. | |
// O(n) | |
template <typename Container> | |
bool is_strictly_sorted(const Container& xs) | |
{ | |
typedef typename Container::value_type T; | |
auto comp = std::less<T>(); | |
return is_strictly_sorted_by(comp, xs); | |
} | |
// API search type: is_sorted_by : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is sorted using a predicate. | |
// comp(a, b) must return true only if a < b. | |
// O(n) | |
template <typename Container, typename Compare> | |
bool is_sorted_by(Compare comp, const Container& xs) | |
{ | |
internal::check_compare_for_container<Compare, Container>(); | |
if (size_of_cont(xs) < 2) | |
return true; | |
auto it1 = std::begin(xs); | |
for (auto it2 = it1 + 1; it2 < std::end(xs); ++it1, ++it2) | |
if (internal::invoke(comp, *it2, *it1)) | |
return false; | |
return true; | |
} | |
// API search type: is_sorted_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a container already is strictly sorted using a transformer. | |
// O(n) | |
template <typename Container, typename F> | |
bool is_sorted_on(F f, const Container& xs) | |
{ | |
return is_sorted_by(is_less_by(f), xs); | |
} | |
// API search type: is_sorted : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if a container already is sorted | |
// in ascending order using std::less. | |
// O(n) | |
template <typename Container> | |
bool is_sorted(const Container& xs) | |
{ | |
typedef typename Container::value_type T; | |
auto comp = std::less<T>(); | |
return is_sorted_by(comp, xs); | |
} | |
// API search type: is_prefix_of : ([a], [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a containers starts with a token. | |
// is_prefix_of("Fun", "FunctionalPlus") == true | |
template <typename Container> | |
bool is_prefix_of(const Container& token, const Container& xs) | |
{ | |
if (size_of_cont(token) > size_of_cont(xs)) | |
return false; | |
return get_segment(0, size_of_cont(token), xs) == token; | |
} | |
// API search type: is_suffix_of : ([a], [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a containers contains a token as a segment. | |
// is_suffix_of("us", "FunctionalPlus") == true | |
template <typename Container> | |
bool is_suffix_of(const Container& token, const Container& xs) | |
{ | |
if (size_of_cont(token) > size_of_cont(xs)) | |
return false; | |
return get_segment(size_of_cont(xs) - size_of_cont(token), | |
size_of_cont(xs), xs) | |
== token; | |
} | |
// API search type: all_by : ((a -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if a containers contains a token as a segment. | |
// all_by(is_even, [2, 4, 6]) == true | |
// Returns true for empty containers. | |
template <typename UnaryPredicate, typename Container> | |
bool all_by(UnaryPredicate p, const Container& xs) | |
{ | |
internal::check_unary_predicate_for_container<UnaryPredicate, Container>(); | |
return std::all_of(std::begin(xs), std::end(xs), p); | |
} | |
// API search type: all : [Bool] -> Bool | |
// fwd bind count: 0 | |
// Checks if all values in a container evaluate to true. | |
// all([true, false, true]) == false | |
// Returns true for empty containers. | |
template <typename Container> | |
bool all(const Container& xs) | |
{ | |
typedef typename Container::value_type T; | |
return all_by(identity<T>, xs); | |
} | |
// API search type: all_the_same_by : (((a, a) -> Bool), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all values in a container are equal using a binary predicate. | |
// Returns true for empty containers. | |
template <typename Container, typename BinaryPredicate> | |
bool all_the_same_by(BinaryPredicate p, const Container& xs) | |
{ | |
internal::check_binary_predicate_for_container<BinaryPredicate, Container>(); | |
if (size_of_cont(xs) < 2) | |
return true; | |
auto unaryPredicate = bind_1st_of_2(p, xs.front()); | |
return all_by(unaryPredicate, xs); | |
} | |
// API search type: all_the_same_on : ((a -> b), [a]) -> Bool | |
// fwd bind count: 1 | |
// Checks if all values in a container are equal using a transformer. | |
// Returns true for empty containers. | |
template <typename Container, typename F> | |
bool all_the_same_on(F f, const Container& xs) | |
{ | |
if (size_of_cont(xs) < 2) | |
return true; | |
auto unaryPredicate = is_equal_by_to(f, f(xs.front())); | |
return all_by(unaryPredicate, xs); | |
} | |
// API search type: all_the_same : [a] -> Bool | |
// fwd bind count: 0 | |
// Checks if all values in a container are equal. | |
// Returns true for empty containers. | |
template <typename Container> | |
bool all_the_same(const Container& xs) | |
{ | |
typedef typename Container::value_type T; | |
auto binaryPredicate = std::equal_to<T>(); | |
return all_the_same_by(binaryPredicate, xs); | |
} | |
// API search type: numbers_step : (a, a, a) -> [a] | |
// fwd bind count: 2 | |
// Return a sequence of numbers using a specific step. | |
// numbers_step(2, 9, 2) == [2, 4, 6, 8] | |
template <typename T, | |
typename ContainerOut = std::vector<T>> | |
ContainerOut numbers_step(const T start, const T end, const T step) | |
{ | |
ContainerOut result; | |
if ((step > 0 && start >= end) || (step < 0 && start <= end) || step == 0) { | |
return result; | |
} | |
std::size_t size = static_cast<std::size_t>((end - start) / step); | |
internal::prepare_container(result, size); | |
auto it = internal::get_back_inserter<ContainerOut>(result); | |
for (T x = start; x < end; x += step) | |
*it = x; | |
return result; | |
} | |
// API search type: numbers : (a, a) -> [a] | |
// fwd bind count: 1 | |
// Return an ascending sequence of numbers.. | |
// Also known as range. | |
// numbers(2, 9) == [2, 3, 4, 5, 6, 7, 8] | |
template <typename T, | |
typename ContainerOut = std::vector<T>> | |
ContainerOut numbers(const T start, const T end) | |
{ | |
return numbers_step<T, ContainerOut>(start, end, 1); | |
} | |
// API search type: singleton_seq : a -> [a] | |
// fwd bind count: 0 | |
// Construct a sequence containing a single value. | |
// singleton_seq(3) == [3]. | |
template <typename T, typename ContainerOut = std::vector<T>> | |
ContainerOut singleton_seq(const T& x) | |
{ | |
return ContainerOut(1, x); | |
} | |
// API search type: all_idxs : [a] -> [Int] | |
// fwd bind count: 0 | |
// Returns a vector containing all valid indices of sequence xs. | |
// all_idxs([6,4,7,6]) == [0,1,2,3] | |
template <typename Container> | |
std::vector<std::size_t> all_idxs(const Container& xs) | |
{ | |
return numbers<std::size_t>(0, size_of_cont(xs)); | |
} | |
// API search type: init : [a] -> [a] | |
// fwd bind count: 0 | |
// init([0,1,2,3]) == [0,1,2] | |
// Unsafe! xs must be non-empty. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut init(Container&& xs) | |
{ | |
assert(!is_empty(xs)); | |
return get_segment(0, size_of_cont(std::forward<Container>(xs)) - 1, xs); | |
} | |
// API search type: tail : [a] -> [a] | |
// fwd bind count: 0 | |
// Drops the first element of a container, keeps the rest. Unsafe! | |
// tail([0,1,2,3]) == [1,2,3] | |
// Unsafe! xs must be non-empty. | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut tail(Container&& xs) | |
{ | |
assert(!is_empty(xs)); | |
return get_segment(1, size_of_cont(std::forward<Container>(xs)), xs); | |
} | |
// API search type: head : [a] -> a | |
// fwd bind count: 0 | |
// Return the first element of a container. | |
// head([0,1,2,3]) == 0 | |
// Unsafe! xs must be non-empty. | |
template <typename Container> | |
typename Container::value_type head(const Container& xs) | |
{ | |
assert(!is_empty(xs)); | |
return xs.front(); | |
} | |
// API search type: last : [a] -> a | |
// fwd bind count: 0 | |
// Return the last element of a container. | |
// last([0,1,2,3]) == 3 | |
// Unsafe! xs must be non-empty. | |
template <typename Container> | |
typename Container::value_type last(const Container& xs) | |
{ | |
assert(!is_empty(xs)); | |
return xs.back(); | |
} | |
// API search type: mean_stddev : [a] -> (a, a) | |
// fwd bind count: 0 | |
// Calculates the mean and the population standard deviation. | |
// mean_stddev([4, 8]) == (6, 2) | |
// mean_stddev([1, 3, 7, 4]) == (3.75, 2.5) | |
// xs must be non-empty. | |
template <typename Result, typename Container> | |
std::pair<Result, Result> mean_stddev(const Container& xs) | |
{ | |
assert(size_of_cont(xs) != 0); | |
// http://stackoverflow.com/a/7616783/1866775 | |
Result sum = static_cast<Result>( | |
internal::accumulate(xs.begin(), xs.end(), | |
static_cast<typename Container::value_type>(0))); | |
Result mean = sum / static_cast<Result>(xs.size()); | |
std::vector<Result> diff(xs.size()); | |
std::transform(xs.begin(), xs.end(), diff.begin(), | |
[mean](Result x) { | |
return x - mean; | |
}); | |
Result sq_sum = std::inner_product( | |
diff.begin(), diff.end(), diff.begin(), static_cast<Result>(0)); | |
Result stddev = std::sqrt(sq_sum / static_cast<Result>(xs.size())); | |
return std::make_pair(mean, stddev); | |
} | |
// API search type: count_occurrences_by : ((a -> b), [a]) -> Map b Int | |
// fwd bind count: 1 | |
// Returns a discrete frequency distribution of the elements in a container | |
// applying a specific transformer. | |
// count_occurrences_by(floor, [1.1, 2.3, 2.7, 3.6, 2.4]) == [(1, 1), (2, 3), (3, 1)] | |
// O(n) | |
template <typename F, typename ContainerIn> | |
auto count_occurrences_by(F f, const ContainerIn& xs) | |
{ | |
using In = typename ContainerIn::value_type; | |
using MapOut = std::map<std::decay_t<internal::invoke_result_t<F, In>>, std::size_t>; | |
internal::trigger_static_asserts<internal::unary_function_tag, F, typename ContainerIn::value_type>(); | |
MapOut result; | |
for (const auto& x : xs) { | |
++result[internal::invoke(f, x)]; | |
} | |
return result; | |
} | |
// API search type: count_occurrences : [a] -> Map a Int | |
// fwd bind count: 0 | |
// Returns a discrete frequency distribution of the elements in a container | |
// applying a specific transformer. | |
// Can be used to create a histogram. | |
// count_occurrences([1,2,2,3,2]) == [(1, 1), (2, 3), (3, 1)] | |
// O(n) | |
template <typename ContainerIn, | |
typename MapOut = typename std::map< | |
typename ContainerIn::value_type, std::size_t>> | |
MapOut count_occurrences(const ContainerIn& xs) | |
{ | |
return count_occurrences_by(identity<typename ContainerIn::value_type>, xs); | |
} | |
// API search type: lexicographical_less_by : (((a, a) -> Bool), [a], [a]) -> Bool | |
// fwd bind count: 2 | |
// Lexicographical less-than comparison using a specific predicate. | |
// lexicographical_less_by((<), [0,1,2,2,4,5], [0,1,2,3,4,5]) == true | |
// lexicographical_less_by((<), "012245", "012345") == true | |
// lexicographical_less_by((<), "01234", "012345") == true | |
// lexicographical_less_by((<), "012345", "01234") == false | |
// lexicographical_less_by((<), "012345", "012345") == false | |
template <typename Container, typename BinaryPredicate> | |
bool lexicographical_less_by(BinaryPredicate p, | |
const Container& xs, const Container& ys) | |
{ | |
internal::check_binary_predicate_for_container<BinaryPredicate, Container>(); | |
auto itXs = std::begin(xs); | |
auto itYs = std::begin(ys); | |
while (itXs != std::end(xs) && itYs != std::end(ys)) { | |
if (internal::invoke(p, *itXs, *itYs)) { | |
return true; | |
} | |
if (internal::invoke(p, *itYs, *itXs)) { | |
return false; | |
} | |
++itXs; | |
++itYs; | |
} | |
if (size_of_cont(xs) < size_of_cont(ys)) { | |
return true; | |
} | |
return false; | |
} | |
// API search type: lexicographical_less : ([a], [a]) -> Bool | |
// fwd bind count: 1 | |
// Lexicographical less-than comparison. | |
// lexicographical_less([0,1,2,2,4,5], [0,1,2,3,4,5]) == true | |
// lexicographical_less("012245", "012345") == true | |
// lexicographical_less("01234", "012345") == true | |
// lexicographical_less("012345", "01234") == false | |
// lexicographical_less("012345", "012345") == false | |
template <typename Container> | |
bool lexicographical_less(const Container& xs, const Container& ys) | |
{ | |
return lexicographical_less_by( | |
is_less<typename Container::value_type>, xs, ys); | |
} | |
// API search type: lexicographical_sort : [[a]] -> [[a]] | |
// fwd bind count: 0 | |
// sort by lexicographical_less | |
template <typename Container, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut lexicographical_sort(Container&& xs) | |
{ | |
typedef typename std::remove_reference<Container>::type::value_type T; | |
return sort_by(lexicographical_less<T>, std::forward<Container>(xs)); | |
} | |
// API search type: replicate : (Int, a) -> [a] | |
// fwd bind count: 1 | |
// Create a sequence containing x n times. | |
// replicate(3, 1) == [1, 1, 1] | |
template <typename T, | |
typename ContainerOut = std::vector<T>> | |
ContainerOut replicate(std::size_t n, const T& x) | |
{ | |
return ContainerOut(n, x); | |
} | |
namespace internal { | |
template <typename UnaryPredicate, typename T> | |
T instead_of_if(internal::reuse_container_t, UnaryPredicate pred, | |
const T& alt, T&& x) | |
{ | |
if (internal::invoke(pred, x)) | |
return alt; | |
else | |
return std::forward<T>(x); | |
} | |
template <typename UnaryPredicate, typename T> | |
T instead_of_if(internal::create_new_container_t, UnaryPredicate pred, | |
const T& alt, const T& x) | |
{ | |
if (internal::invoke(pred, x)) | |
return alt; | |
else | |
return x; | |
} | |
} // namespace internal | |
// API search type: instead_of_if : ((a -> Bool), a, a) -> a | |
// fwd bind count: 2 | |
// Return alt if pred(x), otherwise x itself. | |
template <typename UnaryPredicate, typename T, typename TAlt> | |
auto instead_of_if(UnaryPredicate pred, const TAlt& alt, T&& x) | |
{ | |
return internal::instead_of_if(internal::can_reuse_v<T> {}, | |
pred, alt, std::forward<T>(x)); | |
} | |
// API search type: instead_of_if_empty : ((a -> Bool), [a], [a]) -> [a] | |
// fwd bind count: 2 | |
// Return alt if xs is empty, otherwise xs itself. | |
template <typename Container, typename ContainerAlt, | |
typename ContainerOut = internal::remove_const_and_ref_t<Container>> | |
ContainerOut instead_of_if_empty(const ContainerAlt& alt, Container&& xs) | |
{ | |
return instead_of_if( | |
is_empty<internal::remove_const_and_ref_t<Container>>, | |
alt, std::forward<Container>(xs)); | |
} | |
} // namespace fplus |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment