I wrote a function for vector comparison.
#include <vector>
#include <iostream>
#include <algorithm>
using std::vector;
using std::cout;
bool mycomp(const vector<int>& vi_a, const vector<int>& vi_b){
for(auto x:vi_a) cout << x;
cout << '\n';
return true;
}
int main(){
vector<int> vi1{2,9,8};
vector<int> vi2{3,5,6};
vector<int> vi = min(vi1, vi2, mycomp);
for(auto x:vi) cout << x;
cout << '\n';
}
Output:
356
356
What's weird is that the output is 356, instead of 298. It seems that the two vectors are switched when calling mycomp.
p.s. I'd rather not use lambda here, because mycomp contains more than one line of code, which is more readable this way.
If you take a look at the cppreference documentation for std::min() you will see in the possible implementation that the second element is used as the first element in the comparator.
template<class T, class Compare>
const T& min(const T& a, const T& b, Compare comp)
{
return (comp(b, a)) ? b : a;
}
And when you call min() with the arguments vi2 and vi1, the second argument is passed to the comparator (which in your case is vi2), upon which the comparator returns true (implying that vi2 is the minimum of the two). While doing this the comparator also prints out 356 because that is the first element passed to the comparator according to the possible implementation above.
After that happens you take the presumably smaller of the two vectors, which is the first one passed to the comparator according to the possible implementation above (which is vi2). And you print that out. Therefore you get 356 again.
Note that the reason b is passed as the first argument to std::min is because in the case where the two compare equal the algorithm is required to return a. At the same time it is required to call the comparator only once. Combining these two requirements gives the possible implementation above.
Related
Let's assume I have N random access containers (std::vector and std::array for example) of different types, and that all containers have the same length. I want to write to write a function that prints them in a column-ordered fashion, i.e.:
#include <vector>
#include <iostream>
#include <array>
#include <complex>
constexpr int nr=100;
void print(const std::vector<double>& d1, const std::array<std::complex<double>,nr>& b1, const std::vector<int>& b2, const std::array<double,nr>& d2)
{
for(int i=0; i<nr; ++i)
std::cout<<b1[i]<<" "<<d1[i]<<" "<<b2[i]<<" "<<d2[i]<<"\n";
}
Now assuming that all the containers contain the standard numeric types I could write a variadic template function that would look like this:
template<typename... T>
void vprint(T... cs)
{
constexpr int nc=sizeof...(T);
std::vector<std::vector<std::complex<long double>>> v(nr, std::vector<std::complex<long double>>(nc));
//then fill v recursively with all the cs and print it
}
where I used std::complex<long double> since it would contain any possible number.
However this solution is unsatisfactory since I am allocating extra memory, I am converting some integers spoiling the output (and possibly precision), and, finally, the solution does not work if any of the containers contains a printable type that does not convert trivially to a numeric type.
Any idea on how to write a more general solution?
You don't need to create any temporaries. Here's an implementation that requires at least one container to be present, but you can remove that requirement by adding an empty overload:
// Handles empty parameters by doing nothing
void vprint() {}
// Handle non-empty parameters
template<typename T, typename... Ts>
void vprint(const T& front, const Ts&... cs) {
for (int i = 0; i < front.size(); ++i) {
std::cout << front[i];
((std::cout << ' ' << cs[i]), ...);
std::cout << '\n';
}
}
This makes use of C++17 fold expressions to print the ith element of each container, after printing the first one manually. Handling the first one explicitly also ensures we don't print an extra space at the end (or beginning) of each line without branches.
If you don't have access to C++17, it can still be implemented but it'll be a lot uglier than this.
As per Louis' comment, you could also add an assert before the for loop to make sure all the containers have at least front.size() elements in them for safety:
assert(((cs.size() >= front.size()) && ...));
Change the >= to == for strict equality.
Usage:
int main() {
std::array<int, 4> a{1,2,3,4};
std::vector<int> b{5,6,7,8};
vprint(a, b);
}
Prints
1 5
2 6
3 7
4 8
https://godbolt.org/z/z6s34TaT9
This question already has answers here:
How does comparator function of c++ STL sort work?
(1 answer)
how does sort function in c++ work? [closed]
(2 answers)
Closed 1 year ago.
I am new to using lambda functions in C++. I have been researching the web, and found several articles, explaining the syntax and purpose of lambda function, but I have not come found articles which are clearly giving an explaining how to write the inner logic of a lambda function.
For example
During sorting a vector in c++ in decreasing order:
sort(v1.begin(), v1.end(), [](const int &a, const int &b){return b < a;});
I write the above code. Here, I have a few questions:
Why do I only provide two parameters in the lambda function? Why not three? or why not I give all the n parameter(n is size of vector) and do a logic? I am not trying to find maximum of two elements, I am trying to sort a vector, why should I only consider two values?
Why does a > b gives descending order? Why not b > a? Are there any kind of ordering inside the lambda function?
The return value in the above lambda function is either false(0) or true(1)? Why do I only have to return false(0) or true(1) to sort? Why can't I return a character to sort, like let's suppose for return value 'a' it is ascending and return value 'd' it is descending?
Again
During finding the max even element
itr = max_element(v1.begin(), v1.end(), [](const int &a, const int &b){
if (isEven(a) && isEven(b)) {
return (a < b);
} else
return false;
}
);
I am returning b > a. Rather than a greater than b. ???
Any suggestion would be greatly appreciated.
Your question has nothing to do with lambdas, but with the std::sort function.
Indeed, if you read the documentation about the third parameter (the comparison function, the lambda in your case), it says:
comparison function object which returns ​true if the first argument is
less than (i.e. is ordered before) the second.
The signature of the comparison function should be equivalent to the
following:
bool cmp(const Type1 &a, const Type2 &b);
Indeed, there is not need to create a lambda to pass it as the third parameter. You can pass any function object that receives two arguments of type T (the one of your container's elements) and returns bool.
For example, you can do something like this:
#include <vector>
#include <algorithm>
#include <iostream>
struct {
bool operator () (int a, int b){
return a > b;
}
}my_comp;
int main(){
std::vector<int> v={1,2,3};
std::sort(v.begin(), v.end(), my_comp);
for(auto e:v) std::cout << e << " ";
std::cout << std::endl;
}
Since I am new in competitive programming so I am finding this a bit difficult. I encountered a code and I am not able to figure it out, need some help to understand it.
#include<iostream>
#include<algorithm>
using namespace std;
bool mycompare(int a ,int b){
return a>b;
}
int main(){
int a[]={5,4,3,1,2,6,7};
int n =sizeof(a)/sizeof(int);
sort(a,a+n,mycompare);
for(int i=0; i<n;i++){
cout<<a[i]<<"";
}
return 0;
}
output:
7 6 5 4 3 2 1
How does this code work more specifically what does the mycompare function do in the code?
My doubt is that why haven't we passed any arguments in the mycompare() function inside the main() function since the prototype of the function is
bool mycompare(int a, int b);
A comparison-based sorting algorithm sorts the elements solely by pair-wise comparison, i.e., if a < b holds, then a has to be placed before b.
This is a fine approach, but if you limit yourself to using <, it only allows you to sort elements in an ascending order. What if you want to have them in descending order, or any other ordering? This is where the concept of a Comparator (or a Compare callable in the context of the C++ standard) comes into play: It is a binary predicate bool compare(element a, element b) that is supposed to replace the < operator, i.e., a < b becomes compare(a, b) instead. This generalization allows you to encapsulate all types of orderings, in your question you already provided an example where the comparison uses a greater-than operator >, which gives you the aforementioned descending sorted order.
As for how this works internally in C++, the details can be rather complicated, but you can look at it as this:
mycompare without any parameters is a function pointer, i.e. a pointer to the memory address where the machine code for mycompare starts. You can do something like
auto func_pointer = mycompare;
func_pointer(1, 2); // calls mycompare(1, 2)
By giving this function pointer as a parameter to std::sort, you replace the default < comparison function by your own. The way C++ works internally gives the additional advantage that this function call can most likely be inlined, i.e., the compiler avoids the function call can be avoided by copying the code from mycompare into the std::sort invocation, which can speed up your code significantly.
std::sort takes a RandomIt (random iterator) as the first and second arguments that must satisfy the requirements of ValueSwappable and LegacyRandomAccessIterator. Instead of using a Plain-Old-Array of int, you want to use std::array which can then provide the iterators with the member functions .begin() and .end().
Using a proper container from the C++ standard template library makes sorting with std::sort trivial. You need not even provide a custom compare function to sort in descending order as std::less<int>() is provided for you (though your purpose may be to provide the compare function)
Your prototype for mycompare will work fine as is, but preferably the parameters are const type rather than just type, e.g.
bool mycompare(const int a, const int b)
{
return a > b;
}
The implementation using the array container is quite trivial. Simply declare/initialize your array a and then call std::sort (a.begin(), a.end(), mycompare); A complete working example would be:
#include <iostream>
#include <algorithm>
#include <array>
bool mycompare(const int a, const int b)
{
return a > b;
}
int main (void) {
std::array<int, 7> a = { 5, 4, 3, 1, 2, 6, 7 };
std::sort (a.begin(), a.end(), mycompare);
for (auto& i : a)
std::cout << " " << i;
std::cout << '\n';
}
Example Use/Output
$ ./bin/array_sort
7 6 5 4 3 2 1
Sorting the Plain Old Array*
If you must use a Plain-Old-Array, then you can use plain-old-pointers as your random iterrators. While not a modern C++ approach, you can handle the plain-old-array with std::sort. You can make use of the builtin std::greater<type>() for a descending sort or std::less<type>() for an ascending sort.
An implementation using pointers would simply be:
#include <iostream>
#include <algorithm>
int main (void) {
int a[] = { 5, 4, 3, 1, 2, 6, 7 };
size_t n = sizeof a / sizeof *a;
#if defined (ASCEND)
std::sort (a, a + n, std::less<int>());
#else
std::sort (a, a + n, std::greater<int>());
#endif
for (size_t i = 0; i < n; i++)
std::cout << " " << a[i];
std::cout << '\n';
}
(same output unless -DASCEND is added as a define on the commandline, and then an ascending sort will result from the use of std::less<int>())
Look things over and let me know if you have further questions.
I asked this question before but the suggestions don't work so I have tried to make my question more clear.
I have a function that returns a std::pair<int, double> but the parameters for the function as follow function(int g, double a);. This function then using make_pair to make a pair with the first being the int and the second being the double. It returns the pair.
I want to create another function to use the pair from the previous function and view the data in the pair, i.e. both first and second. Is it possible to pass the return of the previous function as a parameter for my new function so the second function can view the data? I am unsure about the syntax in C++.
It wouldn't be so hard if you rather not talked about the code but started trying to write it:
#include <iostream>
#include <utility>
std::pair<int, double> function(int g, double a)
{
return std::make_pair(g, a); // or return {g, a};
}
void print(std::pair<int, double> pair) // or take by reference-to-const
{
std::cout << pair.first << " " << pair.second << std::endl;
}
int main()
{
print(function(4, 5.5));
return 0;
}
Something's not in order if you're dealing with class templates, but don't know how to write functions.
I have an std::map<mpz_class,int> (for those unfamiliar, mpz_class is a class container for a very large integer number, defined by GMP, Gnu Multiprecision Library). I use a custom Comparator which uses GMP's cmp() function. In the map, I have inserted several std::pair<mpz_class,int> with correct values (they are reasonable when I print them).
However, I noticed map::find was not working correctly, so I printed what the Comparator is comparing. It turns out the second element (key) is always a very wild value integer value, like 128957236027369832796823768439267, way out of scale of the integers I'm working with.
Is there some sort of memory corruption going on that I'm unawares of? Perhaps mpz_class cannot be used in this fashion? How would I work around this problem? I haven't had this problem with other containers so far.
#include <map>
#include <gmpxx.h>
#include <iostream>
struct Equaler {
inline bool operator()(const mpz_class a, const mpz_class b) const {
std::cout << " about to return " << a << "," << b << "," << cmp(a,b) << "\n";
return cmp(a, b);
}
};
int main() {
mpz_class x("38268");
std::map<mpz_class,int,Equaler> map;
map.insert(std::pair<mpz_class,int>(x,42));
map.find(x);
return 0;
}
Output:
about to return 38268,812462232382732367817613904064203084469901797507,-2
The problem is your comparator. std::map expects a comparator which returns true if the first operand should be considered less than the second, and false otherwise. But cmp works differently. It doesn't return a boolean, it returns an integer, in one of three possible states:
negative : lhs < rhs
0 : lhs == rhs
positive : lhs > rhs
However, a negative and a positive integer, in a boolean context, both evaluate to true, so the results of cmp do not convert correctly to what std::map expects. Change this:
return cmp(a, b);
to this:
return cmp(a, b) < 0;