Skip to content

Commit 658bb5b

Browse files
author
ecrypa
committed
implement min_element
1 parent 1cd7d07 commit 658bb5b

File tree

5 files changed

+113
-2
lines changed

5 files changed

+113
-2
lines changed

example/src/list.cpp

+21
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,27 @@ IS_SAME(
657657
/// [transpose]
658658
)
659659

660+
HIDE(
661+
/// [min_element]
662+
using vals = metal::list<const int[9], const int[2], int[2], volatile int[2]>;
663+
664+
// strict (i.e., irreflexive) ordering: _first_ minimum
665+
template<class x, class y>
666+
using smaller = metal::number<(sizeof(x) < sizeof(y))>;
667+
IS_SAME(metal::min_element<vals, metal::lambda<smaller>>, const int[2]);
668+
669+
// reflexive ordering: _last_ minimum
670+
template<class x, class y>
671+
using not_larger = metal::number<(sizeof(x) <= sizeof(y))>;
672+
IS_SAME(metal::min_element<vals, metal::lambda<not_larger>>, volatile int[2]);
673+
674+
// can be used with Numbers; more customizable than metal::min
675+
using nums = metal::numbers<4, 42, 17, 3, 20>;
676+
IS_SAME(metal::min_element<nums, metal::lambda<metal::less>>, metal::number<3>);
677+
IS_SAME(metal::min_element<nums>, metal::number<3>); // use default ordering
678+
/// [min_element]
679+
)
680+
660681
#if !defined(METAL_WORKAROUND)
661682
HIDE(
662683
/// [accumulate]

include/metal/list.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "list/iota.hpp"
2929
#include "list/join.hpp"
3030
#include "list/list.hpp"
31+
#include "list/min_element.hpp"
3132
#include "list/none_of.hpp"
3233
#include "list/partition.hpp"
3334
#include "list/powerset.hpp"

include/metal/list/min_element.hpp

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#ifndef METAL_LIST_MIN_ELEMENT_HPP
2+
#define METAL_LIST_MIN_ELEMENT_HPP
3+
4+
#include "../config.hpp"
5+
#include "../lambda/apply.hpp"
6+
#include "../lambda/lambda.hpp"
7+
#include "../number/if.hpp"
8+
#include "../number/less.hpp"
9+
10+
namespace metal {
11+
/// \cond
12+
namespace detail {
13+
template<class lbd = metal::lambda<metal::less>>
14+
struct _min_folder;
15+
}
16+
/// \endcond
17+
18+
/// \ingroup list
19+
///
20+
/// ### Description
21+
/// Finds a minimum element in a \list according to an ordering relation.
22+
///
23+
/// \note{The _first_ minimum element is returned if the ordering relation is [strict].}
24+
/// [strict]:
25+
/// https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings
26+
///
27+
/// ### Usage
28+
/// For any \list `l` and \lambda `lbd`
29+
/// \code
30+
/// using result = metal::min_element<l, lbd>;
31+
/// \endcode
32+
///
33+
/// \pre: `l` is not empty, and for any two \values `val_i` and `val_j`
34+
/// contained in `l` `metal::invoke<lbd, val_i, val_j>` returns a \number
35+
/// \returns: \value
36+
/// \semantics:
37+
/// If `l` contains elements `val_0, ..., val_k, ..., val_m-1`, then
38+
/// \code
39+
/// using result = val_k;
40+
/// \endcode
41+
/// where `val_k` is an element in `l` such that
42+
/// * `metal::invoke<lbd, val_k, val_j>{} != false` for all `j` in
43+
/// `[0, k)` and
44+
/// * `metal::invoke<lbd, val_i, val_k>{} == false` for all
45+
/// `i` in `(k, m-1]`.
46+
///
47+
/// \tip{`lbd` may be omitted, in which case it defaults to `metal::lambda<metal::less>`.}
48+
///
49+
/// ### Example
50+
/// \snippet list.cpp min_element
51+
///
52+
/// ### See Also
53+
/// \see min, sort
54+
#if !defined(METAL_WORKAROUND)
55+
template<class seq, class lbd = metal::lambda<metal::less>>
56+
using min_element = apply<
57+
lambda<detail::_min_folder<if_<is_lambda<lbd>, lbd>>::template type>,
58+
seq>;
59+
#else
60+
// MSVC 14 has shabby SFINAE support in case of default alias template args
61+
template<class seq, class... lbd>
62+
using min_element = apply<
63+
lambda<detail::_min_folder<if_<is_lambda<lbd>, lbd>...>::template type>,
64+
seq>;
65+
#endif
66+
}
67+
68+
#include "../lambda/invoke.hpp"
69+
#include "../value/fold_left.hpp"
70+
71+
namespace metal {
72+
/// \cond
73+
namespace detail {
74+
template<class lbd>
75+
struct _min_folder {
76+
template<class x, class y>
77+
using custom_min = if_<invoke<lbd, y, x>, y, x>;
78+
// Must use `invoke` in order to support SFINAE. It is tempting to
79+
// pattern-match the `expr` from `lbd` and use `expr<y, x>`, but
80+
// the latter approach spoils SFINAE guarantees.
81+
82+
template<class... vals>
83+
using type = fold_left<lambda<custom_min>, vals...>;
84+
};
85+
}
86+
/// \endcond
87+
}
88+
89+
#endif

include/metal/list/sort.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ namespace metal {
4949
/// \snippet list.cpp sort
5050
///
5151
/// ### See Also
52-
/// \see list, reverse, rotate
52+
/// \see list, min_element, reverse, rotate
5353
#if !defined(METAL_WORKAROUND)
5454
template<class seq, class lbd = metal::lambda<metal::less>>
5555
using sort = detail::call<

include/metal/number/min.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ namespace metal {
3939
/// \snippet number.cpp min
4040
///
4141
/// ### See Also
42-
/// \see number, greater, less, max
42+
/// \see number, greater, less, max, min_element
4343
template<class... nums>
4444
using min = fold_left<lambda<detail::min>, if_<is_number<nums>, nums>...>;
4545
}

0 commit comments

Comments
 (0)