Showing posts with label transform. Show all posts
Showing posts with label transform. Show all posts

C++ example of 'transform'

From the CPP Reference:
#include
  1. output_iterator transform( input_iterator start, input_iterator end, output_iterator result, UnaryFunction f );
  2. output_iterator transform( input_iterator start1, input_iterator end1, input_iterator2 start2, output_iterator result, BinaryFunction f );
The transform algorithm applies the function f to some range of elements, storing the result of each application of the function in result.

The first version of the function applies f to each element in [start,end) and assigns the first output of the function to result, the second output to (result+1), etc.

The second version of the transform works in a similar manner, except that it is given two ranges of elements and calls a binary function on a pair of elements.

Here is an example that is modified from C++ Reference:




//Program tested on Microsoft Visual Studio 2008 - Zahid Ghadialy
//Program to show how transform works in C++

#include<iostream>
#include<vector>
#include<algorithm> //for the transform() function

using namespace
std;

int
increase (int i) { return ++i; }
int
sum (int i, int j) { return i+j; }

int
main()
{

int
someNums[] = {10,20,30,40,50}; //Array of 5 values
//Another way to initialise a vector using the iterator constructor
vector<int> vectorOne (someNums, someNums + sizeof(someNums) / sizeof(int));
vector<int> vectorTwo;
vector<int>::iterator it;

cout<<"vectorOne elements are : ";
for
(unsigned int i = 0; i < vectorOne.size(); ++i)
cout<<vectorOne[i]<<" ";
cout<<endl;

vectorTwo.resize(vectorOne.size()); //allocate space

//OutputIterator transform ( InputIterator first1, InputIterator last1,
// OutputIterator result, UnaryOperator op );
std::transform(vectorOne.begin(), vectorOne.end(), vectorTwo.begin(), increase);

cout<<"\nAfter transform with increase:"<<endl;
cout<<"vectorOne elements are : ";
for
(unsigned int i = 0; i < vectorOne.size(); ++i)
cout<<vectorOne[i]<<" ";
cout<<endl;
cout<<"vectorTwo elements are : ";
for
(unsigned int i = 0; i < vectorTwo.size(); ++i)
cout<<vectorTwo[i]<<" ";
cout<<endl;

//OutputIterator transform ( InputIterator1 first1, InputIterator1 last1,
// InputIterator2 first2, OutputIterator result,
// BinaryOperator binary_op );
transform (vectorOne.begin(), vectorOne.end(), vectorTwo.begin(), vectorOne.begin(), sum);

cout<<"\nAfter transform with sum:"<<endl;
cout<<"vectorOne elements are : ";
for
(unsigned int i = 0; i < vectorOne.size(); ++i)
cout<<vectorOne[i]<<" ";
cout<<endl;
cout<<"vectorTwo elements are : ";
for
(unsigned int i = 0; i < vectorTwo.size(); ++i)
cout<<vectorTwo[i]<<" ";
cout<<endl;

return
0;
}





The output is as follows:

Case insensitive string comparison

While looking up for case insensitive comparison function, I came across this nice article by Matt Austern : Case Insensitive String Comparison). And decided to try to make a sample that does that. Below is the result, am not sure if it is perfect and has no issues but that is the best I could do. Atleast, better than ignoring the locale completely! Yes, ignoring it would work most of the times as it's not needed but just in case, a need came up, what would you do?

I tested the code with VS 2005 (couldn't test it with gcc as I found out that I did not have support for the german locale as I only had the sample strings for that language (out of the above article) and I felt lazy enough to find more sample to test) but if someone finds an issue with some other language words for which your compiler provides locale support, can you please point it out?

Code:

    #include<iostream>
    #include<locale>
    #include<string>
    #include<algorithm>
    #include<functional>
    //used boost::bind due to buggy bind2nd
    //#include<tr1/bind.hpp>
    #include<boost/bind.hpp>

    //using namespace std;
    //using namespace std::tr1;
    //using namespace std::tr1::placeholders;

    using namespace boost;

    struct CaseInsensitiveCompare
    {
        bool operator()(const std::string& lhs, const std::string& rhs)
        {
            std::string lhs_lower;
            std::string rhs_lower;
            //std::transform(lhs.begin(), lhs.end(), std::back_inserter(lhs_lower), std::bind2nd(std::ptr_fun(std::tolower<char>), loc));
            //std::transform(rhs.begin(), rhs.end(), std::back_inserter(rhs_lower), std::bind2nd(std::ptr_fun(std::tolower<char>), loc));
            std::transform(lhs.begin(), lhs.end(), std::back_inserter(lhs_lower), bind(std::tolower<char>, _1, loc));
            std::transform(rhs.begin(), rhs.end(), std::back_inserter(rhs_lower), bind(std::tolower<char>, _1, loc));
            return lhs_lower < rhs_lower;
        }
        CaseInsensitiveCompare(const std::locale& loc_): loc(loc_){}
    private:
        std::locale loc;
    };

    int main()
    {
        std::string lhs = "GEW\334RZTRAMINER";
        std::string rhs = "gew\374rztraminer";
        std::cout << "lhs : " << lhs << std::endl;
        std::cout << "rhs : " << rhs << std::endl;
        CaseInsensitiveCompare cis((std::locale("German_germany")));
        //CaseInsensitiveCompare cis((std::locale()));
        std::cout << "compare result : " << cis(lhs,rhs) << std::endl;
    }

One obvious improvement/alternative could be not copying the strings and instead doing a per character based tolower/toupper and compare them. This has a 2nd advantage as well that it will break out as soon as a mismatch happens for a character, without the need to convert the whole 2 strings into a common case.

Herb Sutter, in one of his Gotw's, writes about a case insensitive string class but also shows the basic problems that would have. Quite simply put, it doesn't work with iostreams (cout/cerr etc). Here: Strings: A case insensitive string class.

Check out this stream