2016-04-28 8 views
0

Ich habe diese STL erklärt multiset:Wie kann ich zwei Arten von Komparatoren (einen zum Einfügen, einen zum Suchen) auf diesem Multiset einstellen?

multiset<IMidiMsgExt, IMidiMsgExtComp> playingNotes; 

und mein Komparator ist:

struct IMidiMsgExtComp { 
    bool operator()(const IMidiMsgExt& lhs, const IMidiMsgExt& rhs) { 
     return lhs.mTick < rhs.mTick; 
    } 
}; 

und dies dient mir gut auf .insert:

playingNotes.insert(midiMessage); 

es Einsätze (und als Aufträge) der Artikel mit der min mTick an der Spitze und der max mTick am Ende der Liste. So ist es geordnet nach mTick, und jeder .begin() wird ein Objekt mit dem min mTick Wert zurückgeben.

Nun würde Ich mag innerhalb dieser Liste finden Sie das erste Element, das haben, auf einem anderen Feld mNote genannt (was int ist), wird der Wert 60, und als sie entfernen:

auto iteratorItemFound = playingNotes.find(60); 
playingNotes.erase(iteratorItemFound); 

aber wie Kann ich festlegen, in welchem ​​Feld die Liste suchen soll? Ein weiterer Komparator?

+0

Sie können 'boost :: multi_index' Array dafür verwenden, oder implementieren Sie das manuell, es sei denn, Sie möchten lineare Suche – Slava

+1

Ich sehe das Update auf die Frage. multi_index ist ein Weg, oder ein sortierter Vektor könnte ein anderer sein. –

Antwort

2

Verwenden Sie std::find_if.

int value = 60; 
auto iteratorItemFound = std::find_if(std::begin(playingNotes), std::end(playingNotes), [value](const IMidiMsgExt& msg) 
{ 
    return msg.mNote == value; 
}); 
+0

find_if hat eine lineare Suchzeit. es vereitelt das Objekt der Verwendung einer Karte. –

+0

Wie kann ich diesen '60'-Wert übergeben? Es ist in einer Variablen außerhalb dieses Bereichs. – markzzz

+0

Ich habe meine Antwort aktualisiert, um Ihre Follow-up-Frage zu beantworten, aber ich muss @Richard Hodges zustimmen. –

0

Was Sie tun möchten, ist nicht möglich (unter Beibehaltung der logarithmischen Sucheigenschaften der Karte). Der Grund dafür ist, dass die Methode find eine Instanz von key_type verwendet, so dass selbst das Schreiben eines benutzerdefinierten Vergleichers, der Überladungen zum Vergleichen Ihres Schlüsseltyps mit Ints aufweist, nicht funktioniert.

Die Idee von key_type ist, dass es ein unveränderliches und billiges Objekt zu konstruieren ist - d. H. Sie sollten keine Angst haben, es als einen Wert zu behandeln, der kopiert werden kann.

Edit:

Hier ist, wie ich es Ansatz kann.

Synopsis:

  1. eine sortierte Vektor verwenden (nach dem Zeitstempel sortiert) als meine 'Set'.
  2. dies ermöglicht es mir, in logarithmischer Zeit, gleiche wie ein Satz
  3. aber ich kann jetzt schnell über den Satz suchen suchen (keine Zeiger de-Referenzierung und guten Cache Lokalität)

Code:

#include <vector> 
#include <algorithm> 

struct midi_message 
{ 
    midi_message(int timestamp, int note) 
    : _timestamp(timestamp) 
    , _note(note) 
    {} 
    int timestamp() const { return _timestamp; } 
    int note() const { return _note; } 
private: 
    int _timestamp, _note; 
}; 

struct earlier 
{ 
    bool operator()(const midi_message& l, const midi_message& r) const { 
    return l.timestamp() < r.timestamp(); 
    } 

    bool operator()(const midi_message& l, const int& r) const { 
    return l.timestamp() < r; 
    } 
}; 

struct midi_messages 
{ 
    // insert messages, keeping the map sorted by timestamp 
    void add(midi_message m) { 
    auto i = std::lower_bound(std::begin(_data), 
            std::end(_data), 
            m.timestamp(), 
            earlier()); 

    _data.insert(i, std::move(m)); 
    } 

    bool remove_first_note_like(int note) 
    { 
    auto i = std::find_if(std::begin(_data), 
          std::end(_data), 
          [note](auto& msg) 
          { return msg.note() == note; }); 
    if (i != std::end(_data)) { 
     _data.erase(i); 
     return true; 
    } 
    return false; 
    } 

    std::size_t remove_all_before(int timestamp) 
    { 
    auto new_begin = std::lower_bound(std::begin(_data), 
             std::end(_data), 
             timestamp, 
             [](auto& msg, auto& timestamp) { 
             return msg.timestamp() < timestamp; 
             }); 
    _data.erase(std::begin(_data), new_begin); 
    } 

    private: 
    std::vector<midi_message> _data; 
}; 

int main() 
{ 
    midi_messages messages; 

    messages.add(midi_message(1000, 60)); 
    messages.add(midi_message(1000, 60)); 
    messages.add(midi_message(1000, 60)); 
    messages.add(midi_message(1001, 60)); 
    messages.add(midi_message(1002, 70)); 
    messages.add(midi_message(1002, 60)); 
    messages.remove_first_note_like(60); 
    messages.remove_all_before(1001); 
} 
+0

Wie würden Sie den Schlüssel so einstellen? Kann 'mNote' nicht zum Schlüssel werden? (auch wenn ich an diesem Punkt Objekte mit demselben Schlüssel haben kann). – markzzz

+0

Ich muss hier etwas falsch verstehen. Wie unterscheidet sich 'midi_messages' von einem 'multiset'? Sie sortieren ihren Inhalt anhand eines Komparators? –

+0

@ JonathanMee es bietet die gleiche Funktionalität mit (wahrscheinlich) bessere Leistung. In allen Tests, die ich jemals ausgeführt habe, übertrifft ein sortierter Vektor einfach set/map/multiset/multimap –

0

Ich würde mit Mohamad Elghawi's answer zustimmen. Aber es ist unvollständig.

Ihr tatsächlicher Code verwenden ein find_if, wie folgt aus:

const auto it = find_if(cbegin(playingNotes), cend(playingNotes), [value = int{60}](const auto& i){return i.mNote == value;}); 

if(it != cend(playingNotes)) { 
    playingNotes.erase(it); 
} 

Dies wird die IMidiMsgExt mit dem niedrigsten Wert mTick entfernen, die eine mNote von 60 hat (oder was auch immer value wurde initialisiert.) Wenn es mehrere s in playingNotes gibt, die für den niedrigsten binden, wird der , der in playingNotes der längste gewesen ist, entfernt.

Du bist Erklärung des Problems war ein wenig spärlich, also habe ich erstellt und löste ein Minimal, Complete, Verifiable Example hier: http://ideone.com/oFQ4rS

0

Lösung mit Boost.MultiIndex:

Live Coliru Demo

#include <boost/multi_index_container.hpp> 
#include <boost/multi_index/ordered_index.hpp> 
#include <boost/multi_index/hashed_index.hpp> 
#include <boost/multi_index/member.hpp> 

using namespace boost::multi_index; 

struct IMidiMsgExt 
{ 
    int mTick; 
    int mTone; 
}; 

using MidiSet=multi_index_container< 
    IMidiMsgExt, 
    indexed_by< 
    ordered_unique<member<IMidiMsgExt,int,&IMidiMsgExt::mTick>>, 
    hashed_non_unique<member<IMidiMsgExt,int,&IMidiMsgExt::mTone>> 
    > 
>; 

#include <iostream> 

int main() 
{ 
    MidiSet m={{0,100},{2,60},{3,150},{5,60},{1,200},{4,90}}; 

    std::cout<<"before erasing:\n"; 
    for(const auto& msg:m)std::cout<<"["<<msg.mTick<<","<<msg.mTone<<"]"; 
    std::cout<<"\n"; 

    m.get<1>().erase(60); 

    std::cout<<"after erasing:\n"; 
    for(const auto& msg:m)std::cout<<"["<<msg.mTick<<","<<msg.mTone<<"]"; 
    std::cout<<"\n"; 
} 

Ausgabe

 
before erasing: 
[0,100][1,200][2,60][3,150][4,90][5,60] 
after erasing: 
[0,100][1,200][3,150][4,90]