// 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 #include #include #include #include namespace fplus { // API search type: apply_to_pair : (((a, b) -> c), (a, b)) -> c // fwd bind count: 1 // Apply binary function to parts of a pair. template auto apply_to_pair(F f, const std::pair& p) { internal::trigger_static_asserts(); return internal::invoke(f, p.first, p.second); } // API search type: zip_with : (((a, b) -> c), [a], [b]) -> [c] // fwd bind count: 2 // Zip two sequences using a binary function. // zip_with((+), [1, 2, 3], [5, 6]) == [1+5, 2+6] == [6, 8] template >, typename ContainerOut = std::vector> ContainerOut zip_with(F f, const ContainerIn1& xs, const ContainerIn2& ys) { internal::trigger_static_asserts(); ContainerOut result; std::size_t resultSize = std::min(size_of_cont(xs), size_of_cont(ys)); internal::prepare_container(result, resultSize); auto itResult = internal::get_back_inserter(result); auto itXs = std::begin(xs); auto itYs = std::begin(ys); for (std::size_t i = 0; i < resultSize; ++i) { *itResult = internal::invoke(f, *itXs, *itYs); ++itXs; ++itYs; } return result; } // API search type: zip_with_3 : (((a, b, c) -> d), [a], [b], [c]) -> [c] // fwd bind count: 3 // Zip three sequences using a ternary function. // zip_with_3((+), [1, 2, 3], [5, 6], [1, 1]) == [7, 9] template < typename ContainerIn1, typename ContainerIn2, typename ContainerIn3, typename F, typename X = typename ContainerIn1::value_type, typename Y = typename ContainerIn2::value_type, typename Z = typename ContainerIn3::value_type, typename TOut = std::decay_t>, typename ContainerOut = typename std::vector> ContainerOut zip_with_3(F f, const ContainerIn1& xs, const ContainerIn2& ys, const ContainerIn3& zs) { internal::trigger_static_asserts(); static_assert(std::is_same< typename internal::same_cont_new_t::type, typename internal::same_cont_new_t::type>::value, "All three Containers must be of same outer type."); static_assert(std::is_same< typename internal::same_cont_new_t::type, typename internal::same_cont_new_t::type>::value, "All three Containers must be of same outer type."); ContainerOut result; std::size_t resultSize = std::min(size_of_cont(xs), size_of_cont(ys)); internal::prepare_container(result, resultSize); auto itResult = internal::get_back_inserter(result); auto itXs = std::begin(xs); auto itYs = std::begin(ys); auto itZs = std::begin(zs); for (std::size_t i = 0; i < resultSize; ++i) { *itResult = internal::invoke(f, *itXs, *itYs, *itZs); ++itXs; ++itYs; ++itZs; } return result; } // API search type: zip_with_defaults : (((a, b) -> c), a, b, [a], [b]) -> [c] // fwd bind count: 4 // Zip two sequences and using a binary function // and extrapolate the shorter sequence with a default value. // zip_with_defaults((+), 6, 7, [1,2,3], [1,2]) == [2,4,10] // zip_with_defaults((+), 6, 7, [1,2], [1,2,3]) == [2,4,9] template < typename ContainerIn1, typename ContainerIn2, typename F, typename X = typename ContainerIn1::value_type, typename Y = typename ContainerIn2::value_type> auto zip_with_defaults(F f, const X& default_x, const Y& default_y, const ContainerIn1& xs, const ContainerIn2& ys) { internal::trigger_static_asserts(); const auto size_xs = size_of_cont(xs); const auto size_ys = size_of_cont(ys); if (size_xs < size_ys) { const auto extended_xs = append( xs, replicate(size_ys - size_xs, default_x)); return zip_with(f, extended_xs, ys); } else if (size_xs > size_ys) { const auto extended_ys = append( ys, replicate(size_xs - size_ys, default_y)); return zip_with(f, xs, extended_ys); } return zip_with(f, xs, ys); } // API search type: zip : ([a], [b]) -> [(a, b)] // fwd bind count: 1 // Combine two sequences to one sequence of pairs. // zip([1, 2, 3], [5, 6]) == [(1, 5), (2, 6)] template auto zip(const ContainerIn1& xs, const ContainerIn2& ys) { auto MakePair = [](const X& x, const Y& y) { return std::make_pair(x, y); }; return zip_with(MakePair, xs, ys); } // API search type: unzip : [(a, b)] -> ([a], [b]) // fwd bind count: 0 // Split a sequence of pairs into two sequences. // unzip([(1, 5), (2, 6)]) == ([1, 2], [5, 6]) template ::type, typename ContainerOutY = typename internal::same_cont_new_t::type> std::pair unzip(const ContainerIn& pairs) { ContainerOutX firsts; ContainerOutY seconds; internal::prepare_container(firsts, size_of_cont(pairs)); internal::prepare_container(seconds, size_of_cont(pairs)); auto itFirsts = internal::get_back_inserter(firsts); auto itSeconds = internal::get_back_inserter(seconds); for (const auto& pair : pairs) { *itFirsts = pair.first; *itSeconds = pair.second; } return std::make_pair(firsts, seconds); } // API search type: fst : ((a, b)) -> a // fwd bind count: 0 // Return the first element of a pair. // fst((0, 1)) == 0 template X fst(const std::pair& pair) { return pair.first; } // API search type: snd : ((a, b)) -> b // fwd bind count: 0 // Return the second element of a pair. // snd((0, 1)) == 1 template Y snd(const std::pair& pair) { return pair.second; } // API search type: transform_fst : ((a -> c), (a, b)) -> (c, b) // fwd bind count: 1 // Apply a function to the first element of a pair. // transform_fst(square, (4, 5)) == (16, 5) template >> std::pair transform_fst(F f, const std::pair& pair) { internal::trigger_static_asserts(); return std::make_pair(internal::invoke(f, pair.first), pair.second); } // API search type: transform_snd : ((b -> c), (a, b)) -> (a, c) // fwd bind count: 1 // Apply a function to the second element of a pair. // transform_snd(square, (4, 5)) == (4, 25) template >> std::pair transform_snd(F f, const std::pair& pair) { internal::trigger_static_asserts(); return std::make_pair(pair.first, internal::invoke(f, pair.second)); } // API search type: transform_pair : ((a -> c), (b -> d), (a, b)) -> (c, d) // fwd bind count: 2 // Apply functions the both parts of a pair. // transform_pair(square, square, (4, 5)) == (16, 25) template < typename X, typename Y, typename F, typename G, typename ResultFirst = std::decay_t>, typename ResultSecond = std::decay_t>> std::pair transform_pair(F f, G g, const std::pair& pair) { internal::trigger_static_asserts(); internal::trigger_static_asserts(); return std::make_pair(internal::invoke(f, pair.first), internal::invoke(g, pair.second)); } // API search type: swap_pair_elems : (a, b) -> (b, a) // fwd bind count: 0 // Swap the first and the second element of a pair. // swap_pair_elems((3,4)) == (4,3) template std::pair swap_pair_elems(const std::pair& pair) { return std::make_pair(pair.second, pair.first); } // API search type: swap_pairs_elems : [(a, b)] -> [(b, a)] // fwd bind count: 0 // Swap the first and the second element of every pair in a sequence. // swap_pairs_elems([(1,2), (3,4)]) == [(2,1), (4,3)] template auto swap_pairs_elems(const ContainerIn& xs) { return fplus::transform(swap_pair_elems, xs); } // API search type: adjacent_pairs : [a] -> [(a, a)] // fwd bind count: 0 // Split a sequence into pairs of elements. // adjacent_pairs([0,1,2,3,4]) == [(0,1), (2,3)] // Also known as zip_with_next. template >::type> ContainerOut adjacent_pairs(const Container& xs) { typedef typename Container::value_type T; static_assert(std::is_convertible< std::pair, typename ContainerOut::value_type>::value, "ContainerOut can not store pairs of elements from ContainerIn."); ContainerOut result; if (size_of_cont(xs) < 2) return result; const std::size_t out_size = size_of_cont(xs) / 2; internal::prepare_container(result, out_size); auto itOut = internal::get_back_inserter(result); auto it1 = std::begin(xs); auto it2 = it1; internal::advance_iterator(it2, 1); const auto it_source_end = internal::add_to_iterator(std::begin(xs), out_size + out_size); for (;;) { *itOut = std::make_pair(*it1, *it2); internal::advance_iterator(it1, 2); if (it1 == it_source_end) break; internal::advance_iterator(it2, 2); } return result; } // API search type: overlapping_pairs : [a] -> [(a, a)] // fwd bind count: 0 // Zip a sequence with itself shifted one element. // overlapping_pairs([0,1,2,3]) == [(0,1),(1,2),(2,3)] template , -1>::type> ContainerOut overlapping_pairs(const Container& xs) { typedef typename Container::value_type T; static_assert(std::is_convertible< std::pair, typename ContainerOut::value_type>::value, "ContainerOut can not store pairs of elements from ContainerIn."); ContainerOut result; if (size_of_cont(xs) < 2) return result; internal::prepare_container(result, size_of_cont(xs) - 1); auto itOut = internal::get_back_inserter(result); auto it1 = std::begin(xs); auto it2 = it1; internal::advance_iterator(it2, 1); for (; it2 != std::end(xs); ++it1, ++it2) { *itOut = std::make_pair(*it1, *it2); } return result; } // API search type: overlapping_pairs_cyclic : [a] -> [(a, a)] // fwd bind count: 0 // Zip a sequence with itself shifted one element, // finally zipping the last element with the first one. // overlapping_pairs_cyclic([0,1,2,3]) == [(0,1),(1,2),(2,3),(3,0)] template , 0>::type> ContainerOut overlapping_pairs_cyclic(const Container& xs) { typedef typename Container::value_type T; static_assert(std::is_convertible< std::pair, typename ContainerOut::value_type>::value, "ContainerOut can not store pairs of elements from ContainerIn."); ContainerOut result; if (size_of_cont(xs) < 2) return result; internal::prepare_container(result, size_of_cont(xs)); auto itOut = internal::get_back_inserter(result); auto it1 = std::begin(xs); auto it2 = it1; internal::advance_iterator(it2, 1); for (; it2 != std::end(xs); ++it1, ++it2) { *itOut = std::make_pair(*it1, *it2); } *itOut = std::make_pair(*it1, xs.front()); return result; } // API search type: enumerate : [a] -> [(Int, a)] // fwd bind count: 0 // Attach its index to every element of a sequence. // enumerate([6,4,7,6]) == [(0, 6), (1, 4), (2, 7), (3, 6)] template auto enumerate(const Container& xs) { return zip(all_idxs(xs), xs); } // API search type: inner_product_with : (((a, a) -> b), ((b, b) -> b), b, [a], [a]) -> b // fwd bind count: 4 // Calculate the inner product of two sequences using custom operations. // inner_product_with((+), (*), [1, 2, 3], [4, 5, 6]) == [32] template < typename ContainerIn1, typename ContainerIn2, typename OP1, typename OP2, typename Acc, typename X = typename ContainerIn1::value_type, typename Y = typename ContainerIn2::value_type, typename OP2Out = internal::invoke_result_t> auto inner_product_with(OP1 op1, OP2 op2, const Acc& value, const ContainerIn1& xs, const ContainerIn2& ys) { internal::trigger_static_asserts(); internal::trigger_static_asserts(); assert(size_of_cont(xs) == size_of_cont(ys)); return std::inner_product( std::begin(xs), std::end(xs), std::begin(ys), value, op1, op2); } // API search type: inner_product : (a, [a], [a]) -> a // fwd bind count: 2 // Calculate the inner product of two sequences. // inner_product([1, 2, 3], [4, 5, 6]) == [32] template Z inner_product(const Z& value, const ContainerIn1& xs, const ContainerIn2& ys) { assert(size_of_cont(xs) == size_of_cont(ys)); return std::inner_product( std::begin(xs), std::end(xs), std::begin(ys), value); } // API search type: first_mismatch_idx_by : (((a, b) -> Bool), [a], [b]) -> Maybe Int // fwd bind count: 2 // Find the first index at which the two sequences differ // using a binary predicate. // first_mismatch_idx_by((==), [1, 2, 3], [1, 4, 3]) == Just 1 // first_mismatch_idx_by((==), [1, 2, 3], [1, 4]) == Just 1 // first_mismatch_idx_by((==), [1, 2, 3], [1, 2]) == Nothing // first_mismatch_idx_by((==), [], [1, 2]) == Nothing template maybe first_mismatch_idx_by(BinaryPredicate p, const ContainerIn1& xs, const ContainerIn2& ys) { auto itXs = std::begin(xs); auto itYs = std::begin(ys); std::size_t minSize = std::min(size_of_cont(xs), size_of_cont(ys)); for (std::size_t i = 0; i < minSize; ++i) { if (!internal::invoke(p, *itXs, *itYs)) { return just(i); } ++itXs; ++itYs; } return nothing(); } // API search type: first_mismatch_by : (((a, b) -> Bool), [a], [b]) -> Maybe (a, b) // fwd bind count: 2 // Find the first pair of elements differing in the two sequences // using a binary predicate. // first_mismatch_by((==), [1, 2, 3], [1, 4, 3]) == Just (2, 4) // first_mismatch_by((==), [1, 2, 3], [1, 4]) == Just (2, 4) // first_mismatch_by((==), [1, 2, 3], [1, 2]) == Nothing // first_mismatch_by((==), [], [1, 2]) == Nothing template > maybe first_mismatch_by(BinaryPredicate p, const ContainerIn1& xs, const ContainerIn2& ys) { const auto maybe_idx = first_mismatch_idx_by(p, xs, ys); if (is_nothing(maybe_idx)) { return nothing(); } else { const auto idx = maybe_idx.unsafe_get_just(); return just(std::make_pair( elem_at_idx(idx, xs), elem_at_idx(idx, ys))); } } // API search type: first_mismatch_idx_on : ((a -> b), [a], [a]) -> Maybe Int // fwd bind count: 2 // Find the first index of elements differing in the two sequences // using a transformer. // first_mismatch_idx_on((mod 2), [1, 2, 3], [3, 5, 3]) == 1 // first_mismatch_idx_on((mod 2), [1, 2, 3], [1, 5]) == 1 // first_mismatch_idx_on((mod 2), [1, 2, 3], [1, 6]) == Nothing // first_mismatch_idx_on((mod 2), [], [1, 2]) == Nothing template > maybe first_mismatch_idx_on(F f, const ContainerIn1& xs, const ContainerIn2& ys) { static_assert(std::is_same::value, "Both containers must have the same element type."); return first_mismatch_idx_by(is_equal_by(f), xs, ys); } // API search type: first_mismatch_on : ((a -> b), [a], [a]) -> Maybe (a, a) // fwd bind count: 2 // Find the first pair of elements differing in the two sequences // using a transformer. // first_mismatch_on((mod 2), [1, 2, 3], [3, 5, 3]) == Just (2, 5) // first_mismatch_on((mod 2), [1, 2, 3], [1, 5]) == Just (2, 5) // first_mismatch_on((mod 2), [1, 2, 3], [1, 6]) == Nothing // first_mismatch_on((mod 2), [], [1, 2]) == Nothing template > maybe first_mismatch_on(F f, const ContainerIn1& xs, const ContainerIn2& ys) { static_assert(std::is_same::value, "Both containers must have the same element type."); return first_mismatch_by(is_equal_by(f), xs, ys); } // API search type: first_mismatch_idx : ([a], [a]) -> Maybe Int // fwd bind count: 2 // Find the first index of elements differing in the two sequences. // first_mismatch_idx((==), [1, 2, 3], [1, 4, 3]) == 1 // first_mismatch_idx((==), [1, 2, 3], [1, 4]) == 1 // first_mismatch_idx((==), [1, 2, 3], [1, 2]) == Nothing // first_mismatch_idx((==), [], [1, 2]) == Nothing template maybe first_mismatch_idx( const ContainerIn1& xs, const ContainerIn2& ys) { static_assert(std::is_same::value, "Both containers must have the same element type."); return first_mismatch_idx_by(std::equal_to(), xs, ys); } // API search type: first_mismatch : ([a], [a]) -> Maybe (a, a) // fwd bind count: 2 // Find the first pair of elements differing in the two sequences // first_mismatch((==), [1, 2, 3], [1, 4, 3]) == Just (2, 4) // first_mismatch((==), [1, 2, 3], [1, 4]) == Just (2, 4) // first_mismatch((==), [1, 2, 3], [1, 2]) == Nothing // first_mismatch((==), [], [1, 2]) == Nothing template > maybe first_mismatch(const ContainerIn1& xs, const ContainerIn2& ys) { static_assert(std::is_same::value, "Both containers must have the same element type."); return first_mismatch_by(std::equal_to(), xs, ys); } // API search type: first_match_idx_by : (((a, b) -> Bool), [a], [b]) -> Maybe Int // fwd bind count: 2 // Find the first index at which the two sequences equal // using a binary predicate. // first_match_idx_by((==), [1, 2, 3], [3, 2, 3]) == Just 1 // first_match_idx_by((==), [], [1, 2]) == Nothing template maybe first_match_idx_by(F f, const ContainerIn1& xs, const ContainerIn2& ys) { auto itXs = std::begin(xs); auto itYs = std::begin(ys); std::size_t minSize = std::min(size_of_cont(xs), size_of_cont(ys)); for (std::size_t i = 0; i < minSize; ++i) { if (internal::invoke(f, *itXs, *itYs)) { return just(i); } ++itXs; ++itYs; } return nothing(); } // API search type: first_match_by : (((a, b) -> Bool), [a], [b]) -> Maybe (a, b) // fwd bind count: 2 // Find the first pair of equal elements in the two sequences // using a binary predicate. // first_match_by((==), [1, 2, 3], [3, 2, 3]) == Just (2, 2) // first_match_by((==), [], [1, 2]) == Nothing template > maybe first_match_by(F f, const ContainerIn1& xs, const ContainerIn2& ys) { const auto maybe_idx = first_match_idx_by(f, xs, ys); if (is_nothing(maybe_idx)) { return nothing(); } else { const auto idx = maybe_idx.unsafe_get_just(); return just(std::make_pair( elem_at_idx(idx, xs), elem_at_idx(idx, ys))); } } // API search type: first_match_idx_on : ((a -> b), [a], [a]) -> Maybe Int // fwd bind count: 2 // Find the first index of equal elements in the two sequences // using a transformer. // first_match_idx_on((mod 2), [1, 2, 3], [2, 4, 3]) == 1 // first_match_idx_on((mod 2), [], [1, 2]) == Nothing template maybe first_match_idx_on(F f, const ContainerIn1& xs, const ContainerIn2& ys) { static_assert(std::is_same::value, "Both containers must have the same element type."); return first_match_idx_by(is_equal_by(f), xs, ys); } // API search type: first_match_on : ((a -> b), [a], [a]) -> Maybe (a, a) // fwd bind count: 2 // Find the first pair of equal elements in the two sequences // using a transformer. // first_match_on((mod 2), [1, 2, 3], [2, 4, 3]) == Just (2, 4) // first_match_on((mod 2), [], [1, 2]) == Nothing template > maybe first_match_on(F f, const ContainerIn1& xs, const ContainerIn2& ys) { static_assert(std::is_same::value, "Both containers must have the same element type."); return first_match_by(is_equal_by(f), xs, ys); } // API search type: first_match_idx : ([a], [a]) -> Maybe Int // fwd bind count: 2 // Find the first index of equal elements in the two sequences. // first_match_idx((==), [1, 2, 3], [5, 2, 3]) == 1 // first_match_idx((==), [], [1, 2]) == Nothing template maybe first_match_idx( const ContainerIn1& xs, const ContainerIn2& ys) { static_assert(std::is_same::value, "Both containers must have the same element type."); return first_match_idx_by(std::equal_to(), xs, ys); } // API search type: first_match : ([a], [a]) -> Maybe (a, a) // fwd bind count: 2 // Find the first pair of equal elements in the two sequences. // first_match((==), [1, 2, 3], [5, 2, 3]) == Just (2, 2) // first_match((==), [], [1, 2]) == Nothing template > maybe first_match(const ContainerIn1& xs, const ContainerIn2& ys) { static_assert(std::is_same::value, "Both containers must have the same element type."); return first_match_by(std::equal_to(), xs, ys); } } // namespace fplus