2012-12-27 52 views
8

Şu anda bu kadar kod ve çalışan var: Ben istiyorumsadece bir kez

string word="test,"; 
string::iterator it = word.begin(); 
for (; it != word.end(); it++) 
{ 
    if (!isalpha(*it)) { 
     break; 
    } 
    else { 
     *it = toupper(*it); 
    } 
} 
word.erase(it, word.end()); 
// word should now be: TEST 

daha kompakt ve bunu okunabilir hale getirmek için:

  1. mevcut standart C Oluşturma ++ sadece bir kez döngü gerçekleştirin
  2. algoritmaları (*)

(*) o c farz ediyorum jrok tarafından önerildiği gibi, özel bir transform_until algoritma tanımlamanın yanı sıra

... Alternatif çözüm

mevcut algoritmaları kodum daha okunabilir hale ombining, kullanarak yineleme olurdu özel bir yineleyici adaptörü tanımlamak mümkün olabilir temel yineleyici, ancak geri döndürmeden önce temel referansı değiştirerek operatörü *() yeniden tanımlar. Böyle şey: bu hala çok ham, ancak fikir vermelidir

template <typename Iterator, typename UnaryFunction = typename Iterator::value_type (*)(typename Iterator::value_type)> 
class sidefx_iterator: public std::iterator< 
         typename std::forward_iterator_tag, 
         typename std::iterator_traits<Iterator>::value_type, 
         typename std::iterator_traits<Iterator>::difference_type, 
         typename std::iterator_traits<Iterator>::pointer, 
         typename std::iterator_traits<Iterator>::reference > 
{ 
    public: 
    explicit sidefx_iterator(Iterator x, UnaryFunction fx) : current_(x), fx_(fx) {} 

    typename Iterator::reference operator*() const { *current_ = fx_(*current_); return *current_; } 
    typename Iterator::pointer operator->() const { return current_.operator->(); } 
    Iterator& operator++() { return ++current_; } 
    Iterator& operator++(int) { return current_++; } 
    bool operator==(const sidefx_iterator<Iterator>& other) const { return current_ == other.current_; } 
    bool operator==(const Iterator& other) const { return current_ == other; } 
    bool operator!=(const sidefx_iterator<Iterator>& other) const { return current_ != other.current_; } 
    bool operator!=(const Iterator& other) const { return current_ != other; } 
    operator Iterator() const { return current_; } 

    private: 
    Iterator current_; 
    UnaryFunction fx_; 
}; 

Elbette. Yukarıdaki adaptör ile , ben sonra aşağıdaki yazabilirsiniz: ile

word.erase(std::find_if(it, it_end, std::not1(std::ref(::isalpha))), word.end()); 

(bazı şablon sihirli basitleşeceğini olabilir) önceden tanımlanmış aşağıdaki: Standart içerecektir

using TransformIterator = sidefx_iterator<typename std::string::iterator>; 
TransformIterator it(word.begin(), reinterpret_cast<typename std::string::value_type(*)(typename std::string::value_type)>(static_cast<int(*)(int)>(std::toupper))); 
TransformIterator it_end(word.end(), nullptr); 

ise Böyle bir adaptör kullanacağım, çünkü bunun kusursuz olduğu anlamına geliyordu, ama bu durum böyle olmadığından belki de döngümüzü olduğu gibi tutacağım. bir adaptör algoritmaları mevcut ve mümkün değil bugün farklı şekillerde karıştırma yeniden sağlayacak, ama daha iyi anlaşılması sonra ... Ben muhtemelen şu anda bakan ediyorum ki, hem

+0

Geçerli kodumda tek bir döngü var. Benim amacım, yeniden yazmanın ardından hala tek bir döngü olacaktı. –

+1

'! Isalpha (* it)' esasına dayanan prematüre çıkış, potansiyel olarak sizi aradığınızı düşündüğüm şeyi elde etmekten alıkoyduğumu gördüğüm tek şey ve dürüstçe, sizin için yapabileceğiniz her şeyi (ve ben hemen hemen her şeyi görürsünüz) büyük ihtimalle bu kadar kıvılcımlı olacaksınız, açıklık faktörü pencereden dışarı çıkacaksınız. Muhtemelen sahip olduklarına bağlı kalırım. – WhozCraig

+0

İlham veren cevaplarınız için hepinize teşekkür ederim. Şu anki kodumla sadık kalacağım. Boost :: transform_iterator'ın aradığım en yakın şey olduğuna inanıyorum. Bu kullanım durumu, "yan etkiler" yineleyici bağdaştırıcısı tarafından, bu şekilde kullanılacak bir şeyle ele alınacaktır: word.erase (std :: find_if (sidefx_iterator (word.begin(), :: toupper), word.end (), std :: not1 (:: isalpha)), word.end()); –

cevap

9

Bunu tek bir standart algoritma ile yapmanın temiz bir yolu olduğunu sanmıyorum.Bildiğim hiçbiri bir yüklemi almaz (erken ayrılmaya karar vermeniz gerekir) ve kaynak dizisinin öğelerini değiştirmeye izin verir.

Gerçekten "standart" şekilde yapmak istiyorsanız kendi genel algoritmanızı yazabilirsiniz. en hmm, transform_until diyelim:

#include <cctype> 
#include <string> 
#include <iostream> 

template<typename InputIt, typename OutputIt, 
     typename UnaryPredicate, typename UnaryOperation> 
OutputIt transform_until(InputIt first, InputIt last, OutputIt out, 
         UnaryPredicate p, UnaryOperation op) 
{ 
    while (first != last && !p(*first)) { 
     *out = op(*first); 
     ++first; 
     ++out; 
    } 
    return first; 
} 

int main() 
{ 
    std::string word = "test,"; 
    auto it = 
    transform_until(word.begin(), word.end(), word.begin(), 
        [](char c) { return !::isalpha(static_cast<unsigned char>(c)); }, 
        [](char c) { return ::toupper(static_cast<unsigned char>(c)); }); 
    word.erase(it, word.end()); 
    std::cout << word << '.'; 
} 

Bunun olması durumunda Bazen döngü düz en iyisi :) ne var daha iyi olup olmadığı tartışılır.

+5

+1, Bu OP'nin alacağı kadar iyi. Muhtemelen A planına bağlı kalmalı ve sadece kendi döngüsünü korurken, bu cevap, özel bir kapsayıcı transformatörün yazılması, herkesin adının uygun bir şekilde yazılması ve OP'nin sorusunu cevaplamak için oldukça iyi bir konsepttir. OP kullanmasa bile, hala iyi bir cevap. – WhozCraig

+0

Bu cevabı kabul ettim çünkü sonuçta ortaya çıkan kod kısa ve okunabilir ve genel olarak yararlı olabilecek genel bir algoritma ifade ediyor. Mevcut tüm STL algoritmalarının sabit bir son yineleyicisi vardır ve tüm kapsayıcı boyunca bir döngü gerektirir. Şartlı bir sona sahip olmak için genel bir ihtiyaç olduğunu düşünüyorum. –

0

olumsuz yanları var olabilir

Böyle Sorunuz, işe yarayabilecek bir fikrim var, ancak Boost gerektirir.

Şunları kullanabilirsiniz tüm karakterler üzerine toupper çağırır ve find_if veya remove_if için inputiterator olarak kullanıp bir transform_iterator. Yine de örnek vermek için Boost ile yeterince tanıdık değilim.

@jrok'un işaret ettiği gibi, transform_iterator değeri yalnızca iterasyon sırasında değiştirir ve orijinal kabı değiştiremez. Bunu aşmak için, aynı sırayla çalışmak yerine, remove_copy_if gibi bir şey kullanarak yeni bir kopyalamak isteyebilirsiniz. Bu, yüklemenin doğru olmadığı sürece kopyalar, bu nedenle std::not1 gerekli olur. Bu, remove_if kasasının yerini alacaktı.

std::copy'u, diğer durumun çalışması için yineleyene kadar std::find_if numaralı telefona dönün. çıktı dize boşsa

Son olarak, bu çıkış için Yineleyici std::inserter tip gerekir.

+0

İki kez döngü yapıyor - OP bunu önlemek istedi. – jrok

+0

Uygulamanızda iki döngü var gibi görünüyor ... Yanlış mıyım? –

+0

@ MariJosé Ah Şimdi sorunuzu daha iyi anladım, evet haklısınız, 2 döngü var. –

İlgili konular