684 lines
25 KiB
C++
684 lines
25 KiB
C++
|
// 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/container_common.hpp>
|
||
|
#include <fplus/function_traits.hpp>
|
||
|
#include <fplus/internal/invoke.hpp>
|
||
|
#include <fplus/internal/asserts/pairs.hpp>
|
||
|
|
||
|
#include <utility>
|
||
|
|
||
|
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 <typename F, typename FIn0, typename FIn1>
|
||
|
auto apply_to_pair(F f, const std::pair<FIn0, FIn1>& p)
|
||
|
{
|
||
|
internal::trigger_static_asserts<internal::apply_to_pair_tag, F, FIn0, FIn1>();
|
||
|
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 ContainerIn1,
|
||
|
typename ContainerIn2,
|
||
|
typename F,
|
||
|
typename X = typename ContainerIn1::value_type,
|
||
|
typename Y = typename ContainerIn2::value_type,
|
||
|
typename TOut = std::decay_t<internal::invoke_result_t<F, X, Y>>,
|
||
|
typename ContainerOut = std::vector<TOut>>
|
||
|
ContainerOut zip_with(F f, const ContainerIn1& xs, const ContainerIn2& ys)
|
||
|
{
|
||
|
internal::trigger_static_asserts<internal::zip_with_tag, F, X, Y>();
|
||
|
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<internal::invoke_result_t<F, X, Y, Z>>,
|
||
|
typename ContainerOut = typename std::vector<TOut>>
|
||
|
ContainerOut zip_with_3(F f,
|
||
|
const ContainerIn1& xs,
|
||
|
const ContainerIn2& ys,
|
||
|
const ContainerIn3& zs)
|
||
|
{
|
||
|
internal::trigger_static_asserts<internal::zip_with_3_tag, F, X, Y, Z>();
|
||
|
static_assert(std::is_same<
|
||
|
typename internal::same_cont_new_t<ContainerIn1, void>::type,
|
||
|
typename internal::same_cont_new_t<ContainerIn2, void>::type>::value,
|
||
|
"All three Containers must be of same outer type.");
|
||
|
static_assert(std::is_same<
|
||
|
typename internal::same_cont_new_t<ContainerIn2, void>::type,
|
||
|
typename internal::same_cont_new_t<ContainerIn3, void>::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<internal::zip_with_tag, F, X, Y>();
|
||
|
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<X, ContainerIn1>(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<Y, ContainerIn2>(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 <typename ContainerIn1, typename ContainerIn2,
|
||
|
typename X = typename ContainerIn1::value_type,
|
||
|
typename Y = typename ContainerIn2::value_type>
|
||
|
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 <typename ContainerIn,
|
||
|
typename TIn = typename ContainerIn::value_type,
|
||
|
typename X = typename TIn::first_type,
|
||
|
typename Y = typename TIn::second_type,
|
||
|
typename ContainerOutX = typename internal::same_cont_new_t<ContainerIn, X>::type,
|
||
|
typename ContainerOutY = typename internal::same_cont_new_t<ContainerIn, Y>::type>
|
||
|
std::pair<ContainerOutX, ContainerOutY> 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 <typename X, typename Y>
|
||
|
X fst(const std::pair<X, Y>& 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 <typename X, typename Y>
|
||
|
Y snd(const std::pair<X, Y>& 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 <typename X, typename Y, typename F,
|
||
|
typename ResultFirst = std::decay_t<internal::invoke_result_t<F, X>>>
|
||
|
std::pair<ResultFirst, Y> transform_fst(F f, const std::pair<X, Y>& pair)
|
||
|
{
|
||
|
internal::trigger_static_asserts<internal::transform_fst_tag, F, X>();
|
||
|
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 <typename X, typename Y, typename F,
|
||
|
typename ResultSecond = std::decay_t<internal::invoke_result_t<F, Y>>>
|
||
|
std::pair<X, ResultSecond> transform_snd(F f, const std::pair<X, Y>& pair)
|
||
|
{
|
||
|
internal::trigger_static_asserts<internal::transform_snd_tag, F, Y>();
|
||
|
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<internal::invoke_result_t<F, X>>,
|
||
|
typename ResultSecond = std::decay_t<internal::invoke_result_t<G, Y>>>
|
||
|
std::pair<ResultFirst, ResultSecond> transform_pair(F f,
|
||
|
G g,
|
||
|
const std::pair<X, Y>& pair)
|
||
|
{
|
||
|
internal::trigger_static_asserts<internal::transform_fst_tag, F, X>();
|
||
|
internal::trigger_static_asserts<internal::transform_snd_tag, G, Y>();
|
||
|
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 <typename X, typename Y>
|
||
|
std::pair<Y, X> swap_pair_elems(const std::pair<X, Y>& 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 <typename ContainerIn,
|
||
|
typename X = typename ContainerIn::value_type::first_type,
|
||
|
typename Y = typename ContainerIn::value_type::second_type>
|
||
|
auto swap_pairs_elems(const ContainerIn& xs)
|
||
|
{
|
||
|
return fplus::transform(swap_pair_elems<X, Y>, 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 <typename Container,
|
||
|
typename ContainerOut =
|
||
|
typename internal::same_cont_new_t<Container,
|
||
|
std::pair<
|
||
|
typename Container::value_type,
|
||
|
typename Container::value_type>>::type>
|
||
|
ContainerOut adjacent_pairs(const Container& xs)
|
||
|
{
|
||
|
typedef typename Container::value_type T;
|
||
|
static_assert(std::is_convertible<
|
||
|
std::pair<T, T>,
|
||
|
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 <typename Container,
|
||
|
typename ContainerOut =
|
||
|
typename internal::same_cont_new_t<Container,
|
||
|
std::pair<
|
||
|
typename Container::value_type,
|
||
|
typename Container::value_type>, -1>::type>
|
||
|
ContainerOut overlapping_pairs(const Container& xs)
|
||
|
{
|
||
|
typedef typename Container::value_type T;
|
||
|
static_assert(std::is_convertible<
|
||
|
std::pair<T, T>,
|
||
|
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 <typename Container,
|
||
|
typename ContainerOut =
|
||
|
typename internal::same_cont_new_t<Container,
|
||
|
std::pair<
|
||
|
typename Container::value_type,
|
||
|
typename Container::value_type>, 0>::type>
|
||
|
ContainerOut overlapping_pairs_cyclic(const Container& xs)
|
||
|
{
|
||
|
typedef typename Container::value_type T;
|
||
|
static_assert(std::is_convertible<
|
||
|
std::pair<T, T>,
|
||
|
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 <typename Container>
|
||
|
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<OP2, X, Y>>
|
||
|
auto inner_product_with(OP1 op1,
|
||
|
OP2 op2,
|
||
|
const Acc& value,
|
||
|
const ContainerIn1& xs,
|
||
|
const ContainerIn2& ys)
|
||
|
{
|
||
|
internal::trigger_static_asserts<internal::inner_product_with_tag, OP2, X, Y>();
|
||
|
internal::trigger_static_asserts<internal::inner_product_with_tag, OP1, Acc, OP2Out>();
|
||
|
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 <typename ContainerIn1, typename ContainerIn2,
|
||
|
typename Z>
|
||
|
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 <typename ContainerIn1, typename ContainerIn2,
|
||
|
typename BinaryPredicate>
|
||
|
maybe<std::size_t> 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<std::size_t>();
|
||
|
}
|
||
|
|
||
|
// 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 <typename ContainerIn1, typename ContainerIn2,
|
||
|
typename BinaryPredicate,
|
||
|
typename X = typename ContainerIn1::value_type,
|
||
|
typename Y = typename ContainerIn2::value_type,
|
||
|
typename TOut = std::pair<X, Y>>
|
||
|
maybe<TOut> 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<TOut>();
|
||
|
}
|
||
|
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 <typename ContainerIn1, typename ContainerIn2,
|
||
|
typename F,
|
||
|
typename X = typename ContainerIn1::value_type,
|
||
|
typename Y = typename ContainerIn2::value_type,
|
||
|
typename TOut = std::pair<X, Y>>
|
||
|
maybe<std::size_t> first_mismatch_idx_on(F f,
|
||
|
const ContainerIn1& xs, const ContainerIn2& ys)
|
||
|
{
|
||
|
static_assert(std::is_same<X, Y>::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 <typename ContainerIn1, typename ContainerIn2,
|
||
|
typename F,
|
||
|
typename X = typename ContainerIn1::value_type,
|
||
|
typename Y = typename ContainerIn2::value_type,
|
||
|
typename TOut = std::pair<X, Y>>
|
||
|
maybe<TOut> first_mismatch_on(F f,
|
||
|
const ContainerIn1& xs, const ContainerIn2& ys)
|
||
|
{
|
||
|
static_assert(std::is_same<X, Y>::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 <typename ContainerIn1, typename ContainerIn2,
|
||
|
typename X = typename ContainerIn1::value_type,
|
||
|
typename Y = typename ContainerIn2::value_type>
|
||
|
maybe<std::size_t> first_mismatch_idx(
|
||
|
const ContainerIn1& xs, const ContainerIn2& ys)
|
||
|
{
|
||
|
static_assert(std::is_same<X, Y>::value,
|
||
|
"Both containers must have the same element type.");
|
||
|
return first_mismatch_idx_by(std::equal_to<X>(), 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 <typename ContainerIn1, typename ContainerIn2,
|
||
|
typename X = typename ContainerIn1::value_type,
|
||
|
typename Y = typename ContainerIn2::value_type,
|
||
|
typename TOut = std::pair<X, Y>>
|
||
|
maybe<TOut> first_mismatch(const ContainerIn1& xs, const ContainerIn2& ys)
|
||
|
{
|
||
|
static_assert(std::is_same<X, Y>::value,
|
||
|
"Both containers must have the same element type.");
|
||
|
return first_mismatch_by(std::equal_to<X>(), 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 <typename ContainerIn1, typename ContainerIn2,
|
||
|
typename F,
|
||
|
typename X = typename ContainerIn1::value_type,
|
||
|
typename Y = typename ContainerIn2::value_type>
|
||
|
maybe<std::size_t> 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<std::size_t>();
|
||
|
}
|
||
|
|
||
|
// 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 <typename ContainerIn1, typename ContainerIn2,
|
||
|
typename F,
|
||
|
typename X = typename ContainerIn1::value_type,
|
||
|
typename Y = typename ContainerIn2::value_type,
|
||
|
typename TOut = std::pair<X, Y>>
|
||
|
maybe<TOut> 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<TOut>();
|
||
|
}
|
||
|
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 <typename ContainerIn1, typename ContainerIn2,
|
||
|
typename F,
|
||
|
typename X = typename ContainerIn1::value_type,
|
||
|
typename Y = typename ContainerIn2::value_type>
|
||
|
maybe<std::size_t> first_match_idx_on(F f,
|
||
|
const ContainerIn1& xs, const ContainerIn2& ys)
|
||
|
{
|
||
|
static_assert(std::is_same<X, Y>::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 <typename ContainerIn1, typename ContainerIn2,
|
||
|
typename F,
|
||
|
typename X = typename ContainerIn1::value_type,
|
||
|
typename Y = typename ContainerIn2::value_type,
|
||
|
typename TOut = std::pair<X, Y>>
|
||
|
maybe<TOut> first_match_on(F f, const ContainerIn1& xs, const ContainerIn2& ys)
|
||
|
{
|
||
|
static_assert(std::is_same<X, Y>::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 <typename ContainerIn1, typename ContainerIn2,
|
||
|
typename X = typename ContainerIn1::value_type,
|
||
|
typename Y = typename ContainerIn2::value_type>
|
||
|
maybe<std::size_t> first_match_idx(
|
||
|
const ContainerIn1& xs, const ContainerIn2& ys)
|
||
|
{
|
||
|
static_assert(std::is_same<X, Y>::value,
|
||
|
"Both containers must have the same element type.");
|
||
|
return first_match_idx_by(std::equal_to<X>(), 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 <typename ContainerIn1, typename ContainerIn2,
|
||
|
typename X = typename ContainerIn1::value_type,
|
||
|
typename Y = typename ContainerIn2::value_type,
|
||
|
typename TOut = std::pair<X, Y>>
|
||
|
maybe<TOut> first_match(const ContainerIn1& xs, const ContainerIn2& ys)
|
||
|
{
|
||
|
static_assert(std::is_same<X, Y>::value,
|
||
|
"Both containers must have the same element type.");
|
||
|
return first_match_by(std::equal_to<X>(), xs, ys);
|
||
|
}
|
||
|
|
||
|
} // namespace fplus
|