Lambda function, arguments and logic in c++ [duplicate] - c++

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;
}

Related

C++ | STL Sort() Function Third Argument Complexity confusions

Suppose, we have a 2D vector<vector<int>> vec;
We need to sort this 2D vector. I tried with two methods below:
Method 1: Using a comparator function
static bool cmp(vector<int> a, vector<int> b) {
return a[1] < b[1];
}
...
sort(vec.begin(),vec.end(),cmp);
Method 2: Using a lambda
sort(vec.begin(), vec.end(), [](const vector<int>& a, vector<int>& b) {
return a[1] < b[1];
});
For a problem from leetcode, Method 1 caused a "Time Limit Exceeded" verdict, while Method 2 was accepted.
Can there be that much contrast between these two methods in terms of time complexity?
vector<int> a makes a copy of the vector while const vector<int>& a just passes the address. Huge difference.
Your comparator is taking its parameters by value, which means every vector object that sort() passes to cmp() will have to be copied in memory. That increases time complexity and memory usage, multiplied out by however many elements are actually in your vectors.
Your lambda, on the other hand, is taking its parameters by reference instead, which means every vector object that sort() passes to the lambda will have only its current memory address passed, no copies are made. So there is no increase in time complexity.
Simply update your comparator to take reference parameters, and then the two methods will have similar complexity:
static bool cmp(const vector<int> &a, const vector<int> &b) {
return a[1] < b[1];
}

how to sort an array in descending order using a boolean function?

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.

C++: Sort a vector<struct> (where struct has 2 integers) based on the one of the integers of struct [duplicate]

This question already has answers here:
Sorting a vector of custom objects
(14 answers)
Closed 3 years ago.
In the below C++ snippet,
HOW TO SORT the vector "TwoIntsVec" BASED ON the element "int a" in TwoInts struct. i.e. i need to place the "TwoIntsVec[i] which has the least "TwoIntsVec[i].a" in the 1st place and so on in increasing order of "TwoIntsVec[i].a".
In the below example the vector elemnt struct having 7,3 should be placed 1st as 7 is the least "a" and so on.
struct TwoInts
{
int a;
int b;
};
void PushToVector(int a, int b, std::vector<TwoInts>& TwoIntsVec)
{
TwoInts temp;
temp.a = a;
temp.b = b;
TwoIntsVec.push_back(temp);
}
int main()
{
std::vector<TwoInts> TwoIntsVec;
PushToVector(21,3,TwoIntsVec);
PushToVector(7,3,TwoIntsVec);
PushToVector(12,3,TwoIntsVec);
PushToVector(9,3,TwoIntsVec);
PushToVector(16,3,TwoIntsVec);
// Below sort would NOT work here, as TwoIntsVec is
// not a std::vector<int>
std::sort( TwoIntsVec.begin(), TwoIntsVec.end());
// HOW TO MAKE THE SORT BASED ON the element "int a" in
TwoInts struct
}
You need to pass an appropriate comparison function to std::sort, as there is no appropriate comparison operator available for TwoInts. See overload #3 here with the description of this comparison parameter:
comp - comparison function object (i.e. an object that satisfies the requirements of Compare) which returns ​true if the first argument is less than (i.e. is ordered before) the second. [...]
One C++11 option is to pass a lambda:
std::sort( TwoIntsVec.begin(), TwoIntsVec.end(),
[](const TwoInts& lhs, const TwoInts& rhs){ return lhs.a < rhs.a; });
If you find that this requires too much typing, you can construct a predicate with Boost HOF like this:
#include <boost/hof/proj.hpp>
#include <boost/hof/placeholders.hpp>
using namespace boost::hof;
std::sort(TwoIntsVec.begin(), TwoIntsVec.end(), proj(&TwoInts::a, _ < _));
Or, as a C++20 teaser:
std::ranges::sort(TwoIntsVec, std::less<>{}, &TwoInts::a);
As a side note, I'd recommend you to fill the vector directly via
// Less complicated than doing the same thing in a function:
TwoIntsVec.push_back({21, 3});
TwoIntsVec.push_back({7, 3});
// ...

possible to move zeros to the end of the array using only std::sort()? [duplicate]

This question already has answers here:
Good C++ solutions to the "Bring all the zeros to the back of the array" interview challenge
(3 answers)
Closed 4 years ago.
I am working on this question:
Given an array nums, write a function to move all 0's to the end of it while maintaining the relative order of the non-zero elements.
I know how to answer this question by just doing in-place swapping, but I also would want to see if it is possible to solve it with std::sort.
According to cplusplus.com:
the comparator function for the sort function is a Binary function that accepts two elements in the range as arguments, and returns a value convertible to bool. The value returned indicates whether the element passed as first argument is considered to go before the second in the specific strict weak ordering it defines.
The function shall not modify any of its arguments.
This can either be a function pointer or a function object.
//comments below are based on my understanding
static bool comp(int a, int b){
//lambda function evaluates to true - no swap,
//evaluates to false -swap
if(a==0) return false;
if(b==0) return true;
//if neither a nor b is 0 them do not swap
return true;
}
void moveZeroes(vector<int>& nums) {
sort(nums.begin(),nums.end(),comp);
}
the given test case is [0,1,0,3,12]
my output is [12,3,1,0,0]
You almost had it right. In your comparator function, you have to return false to not swap them. Also, change std::sort to std::stable_sort to keep the values in original order.
static bool comp(int a, int b)
{
//lambda function evaluates to true - no swap,
//evaluates to false -swap
if(a==0) return false;
if(b==0) return true;
//if neither a nor b is 0 them do not swap
return false;
}
void moveZeros(std::vector<int>& nums)
{
std::stable_sort(nums.begin(),nums.end(),comp);
}
LIVE DEMO
As Drew Dormann pointed out stable partition is the proper algorithm. Here is the code:
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> data { 0, 1, 0, 3, 12 };
std::stable_partition(
data.begin(), data.end(), [](int n) { return n != 0; });
for (auto i : data)
cout << i << ' ';
cout << endl;
}
The output is 1 3 12 0 0
The sort order you want to use is simply that zeros are "greater" than all non-zero values, and equal to other zeros. All other non-zero values are "less" than zero, and are equivalent to any other non-zero value.
Construct the comparison function properly, then you can use it in a call to std::stable_sort to achieve what you're trying to do.

How to write my own functions for comparison?

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.