std::string durchlaufen
-
Hallo,
ich "parse" einen String, der recht einfach aufgebaut ist, er enthält eine Folge von Aminosäuren im Dreibuchstabencode oder als RNS-Codons. D.h. ich muss immer drei Buchstaben auf einmal auslesen und lasse mir über eine Hashtable die korrespondierende Aminosäure ausgeben.
Nun ist eigentlich die Frage, wie ich am elegantesten den String so durchgehe, dass immer drei Buchstaben auf einmal ausgelesen werden. Im Moment mache ich es folgendermaßen, was keinerlei Probleme bereitet:
void AminoSequenceParser::ParseRNACodons(const string& sequence) { for (string::iterator i = sequence.begin(); i != sequence.end(); i = i + 3) { string codon(i, i + 3); if (rnaCodons.find(codon) == rnaCodons.end()) throw InvalidSequenceError(); this->aminoAcids.push_back(rnaCodons[codon]); } }
('rnaCodons' ist eine 'map<string, string>', welche die RNS-Codons als Schlüssel und die Aminosäuren als Werte enthält. 'aminoAcids' ist eine 'list<string>' mit den Aminosäuren.)
Eigentlich finde ich die Lösung intuitiv verständlich und wahrscheinlich ist sie auch recht effizient. 'string::substr' finde ich irgendwie "awkward". Aber mich interessiert mal, ob es eine bessere Möglichkeit gäbe: Man simuliert hier ja im Prinzip ein "Lesefenster" der Breite drei. Gäbe es eine Möglichkeit, das explizit zu programmieren, also im Prinzip als Iterator der Breite drei?
-
Du könntest auch die drei Bytes über c_str() und Offset direkt holen, bzw. adressieren, liegen ja direkt hintereinander. Aber wahrscheinlich ist das eine recht nutzlose Optimierung, solang du nicht das gesamte menschliche Genom parst...
Bye, TGGC (Keine Macht den Dummen)
-
TGGC schrieb:
Du könntest auch die drei Bytes über c_str() und Offset direkt holen, bzw. adressieren, liegen ja direkt hintereinander. Aber wahrscheinlich ist das eine recht nutzlose Optimierung, solang du nicht das gesamte menschliche Genom parst...
Nene, ich parse lediglich codierende Sequenzen, welche genau ein Protein darstellen. Aber nur aus Interesse: Wie würdest Du dann immer drei Bytes auf einmal holen (so, dass die map das verarbeiten kann)?
-
Den Key definieren als struct Key{ char a,b,c;} und dann den Pointer in den String darauf casten sollte gehen.
Bye, TGGC (Keine Macht den Dummen)
-
TGGC schrieb:
Den Key definieren als struct Key{ char a,b,c;} und dann den Pointer in den String darauf casten sollte gehen.
Wow, wenn man noch nen 'operator <' überlädt, klappt das sogar. Aber diese Lösung ist dann doch ein wenig krank.
Viel rumgecaste ...
-
Konrad Rudolph schrieb:
TGGC schrieb:
...
Wow, wenn man noch nen 'operator <' überlädt, klappt das sogar. Aber diese Lösung ist dann doch ein wenig krank.
Viel rumgecaste ...
das ist genau 1 cast...
-
otze schrieb:
Konrad Rudolph schrieb:
TGGC schrieb:
...
Wow, wenn man noch nen 'operator <' überlädt, klappt das sogar. Aber diese Lösung ist dann doch ein wenig krank.
Viel rumgecaste ...
das ist genau 1 cast...
Ja, in der Schleife schon ... aber die Hashtable muss ja mit Strings à drei Buchstaben initialisiert werden. Okok, man kann sich einen impliziten Konstruktor mit einem String als Parameter für diese key-Struktur schreiben. Was mich daran eigentlich am meisten stört, ist eher der Gebrauch von 'c_str()'. Ich überlege im Moment eigentlich, ob es sinnvoll wäre, so ein "CharTriple" mit einem Cast vom String-Iterator zu verwenden.
-
Was mich daran eigentlich am meisten stört, ist eher der Gebrauch von 'c_str()'.
was stört dich daran? bei meiner implementation ist das einfach die rückgabe des internen char pointers des strings...
-
otze schrieb:
Was mich daran eigentlich am meisten stört, ist eher der Gebrauch von 'c_str()'.
was stört dich daran? bei meiner implementation ist das einfach die rückgabe des internen char pointers des strings...
Nun, in 'std::string' ist es ein nullterminiertes char-Array, was auf dem Heap vor sich hin vegetiert und auf Erlösung seitens des std::string wartet. Gut für Interop mit C-Bibliotheken. Wahrscheinlich sage ich hier gerade etwas unheimlich falsches ... aber nullterminierte Strings sind einfach nicht mein Ding. Außerdem finde ich, dass 'c_str()' als Aufruf unheimlich hässlich aussieht. Ich will ja mit der String-Instanz interagieren und mich nicht mit Implementierungsdetails herumschlagen. In meinem Augen ist 'c_str' aber eine Offenlegung von Implementierungsdetails für Rückwärtskompatibilität.
-
Außerdem finde ich, dass 'c_str()' als Aufruf unheimlich hässlich aussieht.
soll es auch. Hierbei bewahrt es uns nur davon, für einen unter umständen recht langen string size()/3 "new" aufzurufen....
dann schaun wir uns das mal an:
struct Codon{ char a,b,c; }; //ich bete zu gott, dass ich endlich mal den richtigen cast verwende const Codon* codons=static_cast<const Codon*>(sequence.c_str()); for(int i=0;i<sequence.size()/3;++i){ if (rnaCodons.find(codons[i]) == rnaCodons.end()) throw InvalidSequenceError(); this->aminoAcids.push_back(rnaCodons[codons[i]]); }
dann ist rnaCodons keine map<string, string> sondern eine map<Codon, string>
ich persönlich würds zwar auch nicht so machen, aber es ist ne alternative
-
otze schrieb:
Außerdem finde ich, dass 'c_str()' als Aufruf unheimlich hässlich aussieht.
soll es auch. Hierbei bewahrt es uns nur davon, für einen unter umständen recht langen string size()/3 "new" aufzurufen....
Hmm, irgendwie hast Du recht, da kann doch schon einiges an Performance rauszuholen sein.
//ich bete zu gott, dass ich endlich mal den richtigen cast verwende
Nope. 'static_cast' geht hier ja gerade nicht, man braucht ein 'reinterpret_cast' ... mit Vorsicht zu genießen.
Allerdings kommen bei mir da jetzt sowieso sehr komische Resultate heraus, egal, wie ich es nun mache.
Ich habe die Klasse 'Codon' ein wenig ausgebaut, damit sie mit der 'map' verwendbar ist aber irgendwo ist da der Wurm drin:
struct Codon { private: char a, b, c; public: Codon(const char* rhs) { this->a = rhs[0]; this->b = rhs[1]; this->c = rhs[2]; } bool operator <(const Codon& rhs) const { return this->a < rhs.a or this->b < rhs.b or this->c < rhs.c; } operator bool() const { return this->a != 0; } };
Angewendet wird das wie folgt: map<Codon, string> rnaCodons; rnaCodons["Tyr"] = "Tyrosine"; rnaCodons["Lys"] = "Lysine"; rnaCodons["His"] = "Histidine"; rnaCodons["Gln"] = "Glutamate"; rnaCodons["Glu"] = "Glutamine"; string foo("LysHisGluGlnTyrTyr"); for (const Codon* i = reinterpret_cast<const Codon*>(foo.c_str()); *i; ++i) std::cout << rnaCodons[*i] << std::endl;
Ausgegeben wird:
Histidine Tyrosine Tyrosine
(Die erste Zeile ist leer, Histidin steht in der zweiten Zeile)
Die letzten beiden Zeilen des Codes kann man auch durch Deine Variante ersetzen, selbes Resultat:
const Codon* codons = reinterpret_cast<const Codon*>(foo.c_str()); for (int i = 0; i < foo.size() / 3; ++i) std::cout << rnaCodons[codons[i]] << std::endl;
-
es liegt an deinem op< der ist falsch.
ich muss leider schnell weg, kann mir deshalb nix ausdenken, hiermit hats aber funktioniert:
bool operator <(const Codon& rhs) const { std::string str1; str1+=a; str1+=b; str1+=c; std::string str2; str2+=rhs.a; str2+=rhs.b; str2+=rhs.c; return str1<str2; }
-
Danke, daran lag's natürlich. Eine Lösung ohne String sieht so aus:
bool operator <(const Codon& rhs) const { return this->a < rhs.a or this->a == rhs.a and (this->b < rhs.b or this->b == rhs.b and this->c < rhs.c); }
-
kleiner tipp: es gibt den op<=
-
otze schrieb:
kleiner tipp: es gibt den op<=
Ja aber da sind die Prioritäten anders. 'and' hat ja eine höhere Priorität als 'or', daher würde '<=' in diesem Kontext etwas anderes heißen.
Mit anderen Worten:
(a <= b and c < d) != (a < b or a == b and c < d)