Grimkin
Grimkin

Reputation: 177

Using range::find on a view

I'd like to rangify the following code, which checks for the first occurance of a sequence of unique characters:

bool hasOnlyUniqueElements( auto& data ) {
    std::unordered_set<char> set;

    for( auto& value : data )
        set.emplace( value );

    return set.size() == data.size();
}

int64_t getStartPacketMarker( const std::string& data, int64_t markerSize ) {
    for( int64_t i = 0; i < data.size() - markerSize; i++ )
    {
        std::string_view packet( data.begin() + i, data.begin() + i + markerSize );
        if( hasOnlyUniqueElements( packet ) )
            return i + markerSize;
    }
    return -1;
}

I came up with the following, that uses ranges but is only marginally better:

int64_t getStartPacketMarker( const std::string& data, int64_t markerSize ) {
    int64_t idx = 0;
    for( auto packet :  data | ranges::views::sliding( markerSize ) ) {
        if( hasOnlyUniqueElements( packet ) )
            return idx + markerSize;
        idx++;
    }

    return -1;
}

This should be a simple find operation, but I couldn't make it work and couldn't find any examples on find being used on views. Is it possible to use find on views?

Upvotes: 1

Views: 706

Answers (2)

Ignacio Gaviglio
Ignacio Gaviglio

Reputation: 56

Besides using std::ranges::find_if on the range you could skip the ´for´ loop that builds the set in hasOnlyUniqueElements using std::unique:

auto set = data;
std::unique(std::sort(set));

Upvotes: 0

Ranoiaetep
Ranoiaetep

Reputation: 6637

Yes, you can use find on views. However, in your case, you should use find_if since you are checking against a predicate function:

auto view = data | std::views::slide(markerSize);
auto it = std::ranges::find_if(
              view, somePredicate
          );
return it == view.end() ? -1 : it - view.begin();

However, since your predicate function has an auto-deduced parameter, you can't get the function pointer of it directly, and you would need to wrap it in a lambda instead:

auto view = data | std::views::slide(markerSize);
auto it = std::ranges::find_if(
              view, [](const auto& v) { return hasOnlyUniqueElements(v); }
          );
return it == view.end() ? -1 : it - view.begin();

Upvotes: 2

Related Questions