简体   繁体   中英

What's the most efficient way to search a set in c++

For example to traverse a string and count the number of vowels I could use

  for (int i=0; (x=inputString[i])!='\0';i++)
    if (x=='a' || x=='e' || x=='i' || x=='o' || x=='u') numVowels++;

In functional languages like haskell or erlang we can do lists:member(x,"aeiou")

In some object oriented languages I can do something like ["a","e","i","o","u"].find(x)

What's the cleanest way to do this in C++ without the x==.. || x==.. || ...

Or

for (auto c: inputString)
{
    c = std::tolower(c);
    if ((c == 'a') || ... (c == 'u'))
    {
        ++numVowels;
    }
}

Just because a language lets you write it in one line doesn't mean it's faster.

If you really, really wanted to get rid of the == you could do

#include <stdint.h>

//                    a  b  c  d  e  ....
const uint8_t TABLE[] = { 1, 0, 0, 0, 1, ... };

for (uint8_t c : inputString)
{
   if (std::isalpha(c))
   {
       numVowels += TABLE[std::tolower(c) - 'a'];
   }
}
inputString.find_first_of("aeiou");

This will give you the first index of any matching element. You can do this in loop to get all matching element:

size_t idx=0;
do{
    size_t current = inputString.find_first_of("aeiou",idx);
    idx = current;
}
while(current!=string::npos);

I'd use this:

#include <algorithm>

auto const numVowels = std::count_if(inputString.begin(), inputString.end(),
                       [](char c) { return c == 'a' || /* ... */ || c == 'u'; })

As far as I know, that is the "best" way to do it. To make it cleaner, you might write a macro or function that just carries out that comparison, ie bool CheckForVowel(char in) or something like that. Then, you could just use that function: if(CheckForVowel(x)){...}

In C++ you could use std::find()() :

std::count_if(str.begin(), str.end(), [](char c) {
        static std::string const vowels("aeiou");
        return vowels.end() != std::find(vowels.begin(), vowels.end(), c);
    });

Sadly, the notation for algorithms doesn't support ranges [yet]. If it did, the operation could be written somewhat nicer as

ranges::count_if(str, [](char c){ return !ranges::find("aeiou"r, c).empty(); });

where r is a suitable user-defined literal producing an character sequence range.

The title ("most efficient") doesn't seem to match the question ("cleanest").

Given that ["a","e","i","o","u"].find(x) counts as "clean", I suppose that the closest C++ equivalent is std::string("aeiou").find(x) != -1 .

I'd also clean up the loop. If you insist on a nul-terminated string for the input, then:

static const std::string lower_vowels("aeiou");
char x;
while ((x = *inputString++)) {
    numVowels += (lower_vowels.find(x) != -1);
}

If you change the input to std::string then:

for (char x : inputString) {
    numVowels += (lower_vowels.find(x) != -1);
}

It's probably not the most efficient, but that's another story. What you have is likely to be pretty close to optimal. I doubt you'll beat it without some fairly ugly code, because what you're basically saying is "can I do better than the compiler at optimizing these 5 comparisons against integral constants?"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM