1

I'm learning C++, and one of the exercices (Accelerated c++) is to code a small program which finds palindromes.

To do this, I've created a vector holding a few words, and I'm then iterating over this vector to find palindromes. While iterating, I'm creating a copy of each word, reversing it, and then comparing if the reversed copy matches the origin word.

using namespace std;
vector<string> palindrome(vector<string> v){
  for(std::vector<string>::iterator iter = v.begin(); iter != v.end(); ++iter){
    string reversed_word;
    for(int i = iter->size(); i >= 0; i--){
      reversed_word += (*iter)[i];
    }
    if (!(*iter).compare(reversed_word)){
      cout << reversed_word << endl;
    }
    cout << reversed_word << endl;
    cout << *iter << endl;
  }
 return v
}


int main(){
  vector<string> dictionnary;
  dictionnary.push_back("coloc");
  dictionnary.push_back("elle");
  dictionnary.push_back("php");
  dictionnary.push_back("shahs");
  dictionnary.push_back("bonjour");
  dictionnary.push_back("random");

  palindrome(dictionnary);
  return 0;
}

However, the condition !(*iter).compare(reversed_word) doens't return what's expected for words that are palindromes. To check, I've displayed the words and the reverse words, and they do match.

What am I missing here ?

Graham Slick
  • 6,692
  • 9
  • 51
  • 87
  • `iter <= v.end()` is seldom right. `end` never points to anything; it is always invalid. The idiom is `!= v.end()`. – Potatoswatter Nov 11 '16 at 16:17
  • palindrome doesn't return anything. I don't think this would compile. – stark Nov 11 '16 at 16:18
  • @Potatoswatter thanks for pointing that out, I didn't change this after switching from an integer to an interator. I'll edit it – Graham Slick Nov 11 '16 at 16:18
  • @stark it does compile, you can test it. I know it doesn't return a vector as it should since I'm not done, I was just testing the comparison of strings `!(*iter).compare(reversed_word)` and realized it didn't work as expected – Graham Slick Nov 11 '16 at 16:19
  • 1
    @GrahamSlick -- *it does compile, you can test it.* -- No. It **must** return a value, else the behavior of the code is undefined. – PaulMcKenzie Nov 11 '16 at 16:21
  • @GrahamSlick The compiler should be warning you about that. Actually it's undefined behavior (possible crash or unpredictability) if the program allows a function to finish without a `return` statement when it doesn't return `void`. – Potatoswatter Nov 11 '16 at 16:21
  • @PaulMcKenzie what I mean is ` it runs `. It is not ideal (and I've edited it), but the program did run. – Graham Slick Nov 11 '16 at 16:22
  • why not `iter->size()` etc. It looks so much nicer – pm100 Nov 11 '16 at 16:23
  • @GrahamSlick What we mean is, you cannot determine definitively that it runs merely by observing it. C++ undefined behavior is nondeterministic. – Potatoswatter Nov 11 '16 at 16:23
  • @pm100 you're right, I'll edit it – Graham Slick Nov 11 '16 at 16:24
  • @Potatoswatter thank you, I'll also edit this – Graham Slick Nov 11 '16 at 16:25
  • @LogicStuff could you please explain how I can improve this ? What do you mean by "write what you're doing" ? – Graham Slick Nov 11 '16 at 16:28
  • @LogicStuff thanks, edited it. diddn't know I could use push_back on a string – Graham Slick Nov 11 '16 at 16:32

4 Answers4

3

How many times does this loop iterate?

for(int i = (*iter).size(); i >= 0; i--)

You probably want size()-1 so it won't look past the end of the word.

Likewise, iter <= v.end() is seldom right. end never points to anything; it is always invalid. The idiom is != v.end().

Also, if the function does not return a value, its return type should be void.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
3

Code like this makes me sad! Even when corrected, it's still (pardon my being frank) ugly and hard to read.

The point of C++ and its standard library is to allow you to work at a relatively abstract level--but most of what you've written is at pretty close to the level of assembly language. Instead of using a vector or a string as an abstraction, you're dealing with individual characters, building up your reversed string one character at a time, and so on.

In terms of abstraction, this is barely baby step above assembly language (and with much uglier syntax than most assemblers use).

So let's sit back and consider what you're really doing.

Your taking a vector of inputs. You're processing each one, and producing a result. That's what std::transform does, so that's what you should probably use here.

std::vector<bool> results(v.size());

std::transform(v.begin(), v.end(), results.begin(), is_palindrome);

So that leaves us with the task of defining is_palindrome.

Here again, I'd advise trying to think at a higher level of abstraction. std::string has a constructor that takes a pair of iterators. std::string also supports reverse iterators--you can use rbegin() and rend() to get reverse iterators into a string.

Between those two things, we can create a reversed string in one fairly simple step:

std::string reversed(s.rbegin(), s.rend());

std::string also overloads most of the relevant operators, so code to compare two strings can be more readable: return s == reversed;

After that, I think it's worth mentioning one or two things that weren't possible when Accelerated C++ was written, but should be on any compiler that's even sort of close to current. The first is initialization lists. Instead of:

  vector<string> dictionnary;
  dictionnary.push_back("coloc");
  dictionnary.push_back("elle");
  dictionnary.push_back("php");
  dictionnary.push_back("shahs");
  dictionnary.push_back("bonjour");
  dictionnary.push_back("random");

...you can now write this like:

vector<string> dictionary{ "coloc", "elle", "php", "shahs", "bonjour", "random" };

Another is lambda expressions. Instead of defining is_palindrome as a function (or functor) we can specify an expression in-place:

std::transform(v.begin(), v.end(),
               results.begin(),
               [](std::string const &s) {
                   return s == std::string(s.rbegin(), s.rend());
               });

There's most of the program in only 5 lines of code--and no (*iter)[i] in sight anywhere.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • "Code like this makes me sad!" I hope people didn't treat you like this when you got started. Do you ever consider that other people don't have as much experience as you do ? As I said in my question, I'm learning c++, it's the 5th chapter so yes, this code is not perfect and might even be bad, but you don't start by writing amazing code, even you. Nonetheless I appreciate your help. – Graham Slick Nov 11 '16 at 17:07
  • @GrahamSlick: I was a little concerned about that. In fact, before you commented, I tried to ask about it: http://chat.stackoverflow.com/transcript/message/34006932#34006932. I assure you, it wasn't intended as an attack on you, just an introduction toward *hopefully* improving it. – Jerry Coffin Nov 11 '16 at 17:10
  • @GrahamSlick well obviously there is a lot to blame on people writing bad books unfortunately. – Loïc Faure-Lacroix Nov 11 '16 at 18:05
  • @LoïcFaure-Lacroix this book is recommended by stack overflow. As I said, did you start by writing amazing and perfectly optimized code ? – Graham Slick Nov 11 '16 at 18:16
  • @LoïcFaure-Lacroix which book would you recommend then ? Or how would you advise to learn c++? – Graham Slick Nov 11 '16 at 18:18
  • @LoïcFaure-Lacroix: The book in question (*Accelerated C++*) is quite good (though dated). I thought it placed a fairly strong emphasis on making good use of containers and algorithms, but my copy is loaned out so I can't look at exactly what it shows by a specific chapter right now. – Jerry Coffin Nov 11 '16 at 18:20
  • @JerryCoffin Algorithms are covered after the 5th chapter so I'm using what's been discussed so far. Do you have any recommendation on how to learn c++ (besides this book) ? – Graham Slick Nov 13 '16 at 09:09
2

Change iter <= v.end(); to iter != v.end();, and

for(int i = (*iter).size(); i >= 0; i--){

to

for(int i = (*iter).size() - 1; i >= 0; i--){

Read this : Why is "!=" used with iterators?

Community
  • 1
  • 1
Saurav Sahu
  • 13,038
  • 6
  • 64
  • 79
1

I believe you are adding an empty character in your first loop, when trying to get (*iter)[(*iter).size()].

If pos is equal to the string length and the string is const-qualified, the function returns a reference to a null character ('\0').

Can you try:

for(int i = (*iter).size()-1; i >= 0; i--){
      reversed_word = reversed_word + (*iter)[i];
    }

std::cout would not show this difference, even though the 2 strings are different.

Jean-Emmanuel
  • 688
  • 8
  • 18