一、极值和比较
在人们刚刚有意识时,就开始有意识的进行比较和最大最小值的操作,比如一个孩子很轻易的可以从一堆苹果中打出一个最大的,从好多的球中找到一个最小的。在和别人的玩耍过程中,知道谁跑得比谁快,谁长得比谁高等等。人类这种意识能不能让机器获得?有谁更好的方式和方法。
数学给了答案,然后进一步应用到计算机中。
二、STL算法的相关算法
在STL中的相关的算法求极值的有:
std::max:求最大值
std::min:求最小值
std::minmax:求最大最小值,返回两个极值
std::max_element:范围内的最大值
std::clamp:判断夹逼位置的比较
比较相关的有:
std::equal:判断是否相等
std::lexicographical_compare:判断指定的字符串的比较结果
其它更多的相关比较和极值算法可参考https://en.cppreference.com/w/cpp/algorithm。
三、源码分析
看一下STL中典型的相关几个函数的源码:
template<class _FwdIt,
class _Pr>
_NODISCARD constexpr pair<_FwdIt, _FwdIt> minmax_element(_FwdIt _First, _FwdIt _Last, _Pr _Pred)
{ // find smallest and largest elements, using _Pred
_Adl_verify_range(_First, _Last);
const auto _Result =
_Minmax_element_unchecked(_Get_unwrapped(_First), _Get_unwrapped(_Last), _Pass_fn(_Pred));
_Seek_wrapped(_Last, _Result.second);
_Seek_wrapped(_First, _Result.first);
return {_First, _Last};
}
template<class _FwdIt,
class _Pr>
constexpr pair<_FwdIt, _FwdIt> _Minmax_element_unchecked(_FwdIt _First, _FwdIt _Last, _Pr _Pred)
{ // find smallest and largest elements, using _Pred
pair<_FwdIt, _FwdIt> _Found(_First, _First);
if (_First != _Last)
{
while (++_First != _Last)
{ // process one or two elements
_FwdIt _Next = _First;
if (++_Next == _Last)
{ // process last element
if (_DEBUG_LT_PRED(_Pred, *_First, *_Found.first))
{
_Found.first = _First;
}
else if (!_DEBUG_LT_PRED(_Pred, *_First, *_Found.second))
{
_Found.second = _First;
}
}
else
{ // process next two elements
if (_DEBUG_LT_PRED(_Pred, *_Next, *_First))
{ // test _Next for new smallest
if (_DEBUG_LT_PRED(_Pred, *_Next, *_Found.first))
{
_Found.first = _Next;
}
if (!_DEBUG_LT_PRED(_Pred, *_First, *_Found.second))
{
_Found.second = _First;
}
}
else
{ // test _First for new smallest
if (_DEBUG_LT_PRED(_Pred, *_First, *_Found.first))
{
_Found.first = _First;
}
if (!_DEBUG_LT_PRED(_Pred, *_Next, *_Found.second))
{
_Found.second = _Next;
}
}
_First = _Next;
}
}
}
return (_Found);
}
template<class _Iter1,
class _Iter2,
class _Pr> inline
false_type _Equal_memcmp_is_safe(const _Iter1&, const _Iter2&, const _Pr&)
{ // return equal optimization category for arbitrary iterators
return {};
}
template<class _InIt1,
class _InIt2,
class _Pr> inline
bool _Equal_unchecked(const _InIt1 _First1, const _InIt1 _Last1,
const _InIt2 _First2, _Pr _Pred)
{ // compare [_First1, _Last1) to [_First2, ...) using _Pred, choose optimization
return (_Equal_unchecked1(_First1, _Last1, _First2, _Pred,
_Equal_memcmp_is_safe(_First1, _First2, _Pred)));
}
msvc的库中乱七八糟的宏定义和检查参数太多,其实是不太适合看相关源码的,但这玩意儿确实是又是最容易获得的,只好如此。
四、实例
看一下几个相关的实例(cppreference.com):
#include <algorithm>
#include <iostream>
#include <vector>
#include <cmath>
static bool abs_compare(int a, int b)
{
return (std::abs(a) < std::abs(b));
}
int main()
{
std::vector<int> v{ 3, 1, -14, 1, 5, 9 };
std::vector<int>::iterator result;
result = std::max_element(v.begin(), v.end());
std::cout << "max element at: " << std::distance(v.begin(), result) << '\n';
result = std::max_element(v.begin(), v.end(), abs_compare);
std::cout << "max element (absolute) at: " << std::distance(v.begin(), result) << '\n';
}
运行结果:
max element at: 5
max element (absolute) at: 2
再看一个c++17以上版本的:
#include <cstdint>
#include <algorithm>
#include <iostream>
#include <iomanip>
int main()
{
std::cout << " raw clamped to int8_t clamped to uint8_t\n";
for(int const v: {-129, -128, -1, 0, 42, 127, 128, 255, 256}) {
std::cout << std::setw(04) << v
<< std::setw(20) << std::clamp(v, INT8_MIN, INT8_MAX)
<< std::setw(21) << std::clamp(v, 0, UINT8_MAX) << '\n';
}
}
输出:
raw clamped to int8_t clamped to uint8_t
-129 -128 0
-128 -128 0
-1 -1 0
0 0 0
42 42 42
127 127 127
128 127 128
255 127 255
256 127 255
再看比较相关:
#include <algorithm>
#include <iostream>
#include <string_view>
constexpr bool is_palindrome(const std::string_view& s)
{
return std::equal(s.begin(), s.begin() + s.size()/2, s.rbegin());
}
void test(const std::string_view& s)
{
std::cout << "\"" << s << "\" "
<< (is_palindrome(s) ? "is" : "is not")
<< " a palindrome\n";
}
int main()
{
test("radar");
test("hello");
}
输出结果:
"radar" is a palindrome
"hello" is not a palindrome
字符串比较:
#include <algorithm>
#include <iostream>
#include <vector>
#include <random>
int main()
{
std::vector<char> v1 {'a', 'b', 'c', 'd'};
std::vector<char> v2 {'a', 'b', 'c', 'd'};
std::mt19937 g{std::random_device{}()};
while (!std::lexicographical_compare(v1.begin(), v1.end(),
v2.begin(), v2.end())) {
for (auto c : v1) std::cout << c << ' ';
std::cout << ">= ";
for (auto c : v2) std::cout << c << ' ';
std::cout << '\n';
std::shuffle(v1.begin(), v1.end(), g);
std::shuffle(v2.begin(), v2.end(), g);
}
for (auto c : v1) std::cout << c << ' ';
std::cout << "< ";
for (auto c : v2) std::cout << c << ' ';
std::cout << '\n';
}
输出结果:
a b c d >= a b c d
d a b c >= c b d a
b d a c >= a d c b
a c d b < c d a b
以上示例均可在std::algorithm中找到。
五、总结
看到这里,可能大家都明白了,为什么现在好多编程的程序员,其实不会“编程”,因为大量的底层逻辑被抽象和总结成库了,这样,好多的程序编写起来就会不断的优化,最终就像c++的STL库一样,不断的迭代,眼看着就c++20,23等等了。没有什么可以多说的,认真学习,不断进步,不断超越自己,才是本分!


本文介绍了如何通过C++ STL库中的max, min, minmax, max_element和clamp等函数实现极值和比较操作。通过实例展示了如何在实际编程中使用这些算法,包括标准库源码解析,并探讨了编程中库抽象的重要性。
1万+

被折叠的 条评论
为什么被折叠?



