2
\$\begingroup\$

Continuing with my series of cipher programs, I hastily wrote a null cipher program, and I was wondering how it could be improved:

#include <iostream>
#include <vector>
#include <sstream>
#include <iterator>
#include <algorithm>

std::string getString();
std::vector<std::string> tokenize(const std::string & str);
std::vector<int> getPattern();

int main(){
    std::string input;
    std::vector<std::string> words;
    std::vector<int> pattern;
    do{
        if(words.size() != pattern.size()){
            std::cout << "\nError: Number of words must be equal to number of integers in pattern.";
        }
        input = getString();
        words = tokenize(input);
        pattern = getPattern();
    } while(words.size() != pattern.size());

    for(int i = 0; i < words.size(); ++i){
        if(words[i].length() > pattern[i]){
            std::string str = words[i];
            std::cout << str[pattern[i]];
        }
    }

    std::cout << '\n';
    return 0;
}

std::string getString(){
    std::cout << "Seperate words with spaces.\nInput string: ";
    std::string input;
    std::getline(std::cin, input);
    return input;
}

std::vector<std::string> tokenize(const std::string & str){
    std::stringstream ss(str);
    std::istream_iterator<std::string> begin(ss);
    std::istream_iterator<std::string> end;
    std::vector<std::string> words(begin, end);
    return words;
}

std::vector<int> getPattern(){
    std::string pattern;
    std::vector<int> intTokenizedPattern;
    bool validPattern;
    do{
        std::cout << "Seperate positive integers with spaces: ";
        std::getline(std::cin, pattern);
        std::vector<std::string> tokenizedPattern = tokenize(pattern);
        try{
            std::transform(tokenizedPattern.begin(), tokenizedPattern.end(), 
                std::back_inserter(intTokenizedPattern), 
                [](const std::string & str){ return (std::stoi(str) > 0 ? std::stoi(str) - 1 : throw "Input less than one");});
            validPattern = true;
        } catch(...){
            tokenizedPattern.clear();
            intTokenizedPattern.clear();
            validPattern = false;
        }
    } while(!validPattern);

    return intTokenizedPattern;
}

Here is an example input/output:

Seperate words with spaces.
Input string: Susan says Gail lies. Matt lets Susan feel jovial. Elated angry?
Seperate positive integers with spaces: 1 2 3 1 2 3 1 2 3 1 2
SailatSevEn

In the output above, it is deciphered to say "sail at seven" (null ciphers are not used in the modern age of cryptography).

In my program, I verify that the number of elements in the words and pattern vectors is equal. I also verify that the elements in the pattern vector are all actual integers (which is done in the try-catch loop).

I found it interesting that at this time this is the only null cipher program on Code Review, so it could also prove helpful to future viewers.

How can this code be improved?

\$\endgroup\$
3
  • \$\begingroup\$ Are negative numbers or 0 valid inputs? If not, then you should catch that at the point of entry and signal an error to the user. \$\endgroup\$
    – Cody Gray
    Jan 18 '17 at 8:49
  • \$\begingroup\$ @CodyGray Any numbers less than 1 produce an error. \$\endgroup\$
    – esote
    Jan 18 '17 at 12:44
  • \$\begingroup\$ @CodyGray Upon giving it further consideraion, I have fixed that issue. The code in my question has been updated. It's not a pretty solution, but it works. \$\endgroup\$
    – esote
    Jan 18 '17 at 12:57
1
\$\begingroup\$

This seems like a waste of time:

std::vector<std::string> tokenize(const std::string & str){
    std::stringstream ss(str);
    std::istream_iterator<std::string> begin(ss);
    std::istream_iterator<std::string> end;
    std::vector<std::string> words(begin, end);
    return words;
}

The result is used here:

std::vector<std::string> tokenizedPattern = tokenize(pattern);
std::transform(tokenizedPattern.begin(), tokenizedPattern.end(), 
               std::back_inserter(intTokenizedPattern), 
               [](const std::string & str){ return stuff;});

You use an iterator to copy it into a vector so you can get the iterator to use in a standard algorithm. Why not just use the algorithm with the original iterator.

std::stringstream streamPattern(pattern);
std::transform(std::istream_iterator<std::string>{streamPattern},
               std::istream_iterator<std::string>{}, 
               std::back_inserter(intTokenizedPattern), 
               [](const std::string & str){ return stuff;});
\$\endgroup\$
2
  • \$\begingroup\$ Using the code as you provide it causes an error: "error: no matching function for call to ‘std::istream_iterator<std::__cxx11::basic_string<char> >::istream_iterator(<brace-enclosed initializer list>)’ std::transform(std::istream_iterator<std::string>{pattern}," \$\endgroup\$
    – esote
    Jan 19 '17 at 3:47
  • \$\begingroup\$ Oops. Did not read your source closely enough. Updated. \$\endgroup\$ Jan 19 '17 at 17:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.