2016-11-08 2 views
1

Ich fordere mich selbst heraus, einen Palindrome-Tester zu schreiben, der nur SL-Algorithmen, Iteratoren usw. verwendet. Ich möchte auch mit rohen Strings arbeiten. Im Folgenden verwende ich den rohen Zeiger pal im copy_if Algorithmus, aber stattdessen, wie könnte ich einen Iterator definieren, um hier zu gehen, d. H. Mit etwas wie begin(pal) und end(pal + size)?Erhalte einen Iterator von einem Char-Zeiger (C++)

#include <algorithm> 
#include <iterator> 
#include <cctype> 

using namespace std; 

bool isPalindrome(const char* pal) { 
    if (!pal) { return(false); } 

    int size = strlen(pal); 
    string pal_raw; 
    pal_raw.reserve(size); 

    // Copy alphabetical chars only (no spaces, punctuations etc.) into pal_raw 
    copy_if(pal, pal+size, back_inserter(pal_raw), 
     [](char item) {return isalpha(item); } 
    ); 

    // Test if palindromic, ignoring capitalisation 
    bool same = equal(begin(pal_raw), end(pal_raw), rbegin(pal_raw), rend(pal_raw), 
     [](char item1, char item2) {return tolower(item1) == tolower(item2); } 
    ); 

    return same; 
} 

int main(){ 
    char pal[] = "Straw? No, too stupid a fad. I put soot on warts."; 
    bool same = isPalindrome(pal); 
    return 0; 
} 

Bonus Frage: Ist es möglich, die Notwendigkeit zu copy_if() durch Erhöhen der Iteratoren ‚anstelle‘ zu eliminieren aus equal() das heißt, wenn !isalpha(item)?

+4

Warum nicht 'std :: string' anstelle eines char-Arrays verwenden? – NathanOliver

+0

@NathanOliver Ich habe schon eine Überladung für 'std :: string' geschrieben, wollte auch in der Lage sein, char Arrays zu handhaben. –

+1

Ein Zeiger bereits * ist * ein Iterator. –

Antwort

3

Iteratoren implementieren das Konzept von Zeigern, wenn es um C++ - Bibliotheksalgorithmen geht. Und wie Sie festgestellt haben, sind C++ - Bibliotheksalgorithmen, die Iteratoren verwenden, auch gerne Zeiger. Es ist das gleiche Konzept.

Und wenn Sie bereits Zeiger haben, gibt es keinen Iterator irgendeiner Art, in den die Zeiger konvertiert werden können.

Es ist wahr, dass

std::begin(arr) 

und

std::end(arr) 

auf ebenen Arrays definiert sind. Aber, raten Sie mal: Sie geben einen Zeiger auf den Anfang und das Ende des Arrays zurück und nicht auf eine Art von Iterator.

jedoch nicht std::begin() verwenden können, und std::end() weil durch die Zeit, die Sie brauchen, es zu benutzen, in Ihrer Funktion wurde das Array bereits zu einem char * abgeklungen ist. std::begin() und std::end() arbeitet auf echten Arrays und nicht verfallenen Zeigern.

Wenn Sie darauf bestehen, Iteratoren zu verwenden, sollten Sie eine std::string an Ihre Palindrome-Funktion übergeben, anstatt eine char *. std::string implementiert eine begin() und eine end() Methode, die eine std::string::iterator zurückgeben, die Sie verwenden können.

2

Wenn ich das tun würde, und ich würde wollen, dass es für verschiedene Arten funktioniert, würde ich die Methode templatize - zum Beispiel:

template<class ITER_T> 
bool isPalindrome(ITER_T begin, ITER_T end) { 
    // check for palindrome generically between begin and end 
} 

diese Weise ist es für const char * Iteratoren arbeiten würde, std::string , std::vector<char>, std::map<char> mit dem gleichen Code. Und wenn es richtig implementiert wird, auch für andere Arten von Vektoren, Karten und alles andere, für das Sie einen Iterator erhalten können, und für den Elementtyp ist ein Vergleichsoperator definiert.

Als Bonus könnte ich dann auch überprüfen, ob ein Teil des Zeichens Array, String oder ein Vektor ein Palindrom ist.

By the way, dies:

equal(begin(pal_raw), end(pal_raw), rbegin(pal_raw), rend(pal_raw), ... 

jedes Zeichen zweimal unnötig überprüft, wenn die Zeichenfolge ein Palindrom ist ... nicht sehr effizient. In diesem Fall könnten Sie vielleicht besser mit einer "manuellen" Schleife machen (nicht sicher, ob es einen std Algo dafür gibt).


Wenn Sie beginnen machen möchten (arr)/Ende (arr) arbeiten in der überladene Funktion, müssten Sie es trotzdem templatize, wie folgt aus:

template<size_t N> 
bool isPalindrome(const char (&arr)[N]) { 
... 

jedoch dann erhalten Sie eine separate Instanziierung für jede andere Array-Größe. Es ist also besser, die Iteratoren zu templatisieren und nur eine Instanz für jede Char-Array-Größe zu erhalten.


So die „Bonus-Frage“ zu beantworten, ist es in der Tat möglich ist, die temporäre Zeichenfolge (dh dynamische Speicherzuweisung) überhaupt zu vermeiden, zu schaffen, indem sie direkt über das Array iterieren:

template<typename ITER_T> 
bool isPalindrome(ITER_T begin, ITER_T end) { 
    while (begin < end) { 
     if (tolower(*begin++) != tolower(*--end)) 
      return false; 
    } 
    return true; 
} 
bool same = isPalindrome(begin(pal), end(pal)); 

zu Test für isalpha Ich überlasse es Ihnen für Ihre Praxis. Hinweis: Sie können dies vor der Gleichheitsüberprüfung tun und das Anfangs-/Ende entsprechend erhöhen/verringern (Hinweis # 2: Die Lösung, die ich im Sinn habe, verwendet das Schlüsselwort continue).

Dasselbe, um es mit einem beliebigen anderen Typ als char arbeiten zu lassen - dann können Typmerkmale verwendet werden, um die Isalpha/Tolower-Aufrufe über Vorlagenspezialisierungen zu abstrahieren.

+0

Es könnte sogar mit 'wstring' und' vector 'funktionieren (vorausgesetzt, dass letzteres natürlich ASCII-Zeichen enthielt). Solange das Aufrufen von 'std :: tolower' für den Elementtyp in Ihrem Kontext sinnvoll ist. – SirGuy

+0

Ja, und der Aufruf von 'tolower()' kann durch Typeigenschaften abstrahiert werden, sodass die Identität für Typen zurückgegeben wird, die keine "Kleinbuchstaben" -Umsetzung unterstützen (zum Beispiel 'vector ' überprüft Palindrom eines beliebigen ganzzahligen Arrays). – axalis

+0

@axalis Ich habe noch keine Typeigenschaften abgedeckt, würde das verhindern, dass ein Palindrom wahr zurückkehrt, wenn es Großbuchstaben enthält? Ja, wie du gesagt hast, wäre es effizienter, wenn der Algorithmus jedes Zeichen nur einmal überprüft. Allerdings ist es schwierig zu tun, da das Ende() nicht unbedingt in der Mitte ist (stell dir eine Zeichenkette vor, bei der die erste Hälfte des Zeichens Leerzeichen waren) - ich weiß, dass ich sowieso den ganzen Platz entferne, aber ich arbeite auf eine Lösung hin Ich vermeide jegliches zusätzliche Kopieren/Entfernen und überspringe stattdessen die Nicht-Alpha-Zeichen, indem ich die Iteratoren inkrementiere –