Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Logic error with std::replace

today I've encountered a logic error while working on a school project. For this project, switching chars around is required.

Say for example, the user enters the letter A. it is switched to U, and all is well. However, the issue occurs when the user enters the letter U. The character remains untouched. I've implemented two rules to switch from A to U and U to A. The string passes through both of the rules, and therefore is unchanged. How can I prevent this?

#include <iostream>
#include <algorithm>
#include <string>

using namespace std;

int main()
{
 string seq;
 getline(cin, seq);

 transform ( seq.begin(), seq.end(), seq.begin(), ::toupper );

 replace ( seq.begin(), seq.end(), 'A', 'U' );
 replace ( seq.begin(), seq.end(), 'U', 'A' );

 cout << seq;
 return 0;
}
like image 323
Koi Avatar asked Sep 25 '26 01:09

Koi


2 Answers

With your implementation, you replace A to U then U back to A:

replace ( seq.begin(), seq.end(), 'A', 'U' );
replace ( seq.begin(), seq.end(), 'U', 'A' );

You need to make your replacements in one pass, for instance with std::transform:

std::transform(begin(seq), end(seq), begin(seq), some_function_object);

You just have to write this mysterious some_function_object. It can be a free-function:

char some_function_object(char input) { /* ... */ }
std::transform(begin(seq), end(seq), begin(seq), some_function_object);

or a lambda function:

std::transform(begin(seq), end(seq), begin(seq), [](char input) { /* ... */ } );

This function object must take a char and return:

  • A if input is U;
  • U if input is A;
  • input otherwise.

And how should you call it if its a free function? Well, why not a self-descriptive name like swapAandU?

like image 104
YSC Avatar answered Sep 26 '26 13:09

YSC


It seems it is the case when a range-based for loop is better suitable and more efficient than using a standard algorithm as for example std::transform.

For example

#include <iostream>
#include <string>

int main() 
{
    std::string s( "AUAUA" );

    std::cout << s << std::endl;

    for ( char &c : s )
    {
        if ( c == 'A' ) c = 'U';
        else if ( c == 'U' ) c = 'A';
    }

    std::cout << s << std::endl;

    return 0;
}

The program output is

AUAUA
UAUAU

You can write a separate function as for example

#include <iostream>
#include <string>

std::string & convert( std::string &s1, const std::string &s2, const std::string &s3 )
{
    for ( char &c : s1 )
    {
        auto i = s2.find( c );
        if ( i != std::string::npos ) c = s3[i]; 
    }

    return s1;
}

int main() 
{
    std::string s( "AUAUA" );

    std::cout << s << std::endl;

    std::cout << convert( s, "AU", "UA" ) << std::endl;

    return 0;
}

If the string s2 is sorted then you can use the binary search algorithm instead of the linear search.

If you want to use a standard algorithm then in my opinion the most suitable algorithm is std::for_each that correcponds to the range-based for loop. For example the first demonstrative program can be rewritten using the algorithm the following way

#include <iostream>
#include <string>
#include <algorithm>

int main() 
{
    std::string s( "AUAUA" );

    std::cout << s << std::endl;

    std::for_each( s.begin(), s.end(), 
        []( char &c ) 
        { 
            if ( c == 'A' ) c = 'U';
            else if ( c == 'U' ) c = 'A';
        } );

    std::cout << s << std::endl;

    return 0;
}
like image 25
Vlad from Moscow Avatar answered Sep 26 '26 13:09

Vlad from Moscow



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!