2012-12-27 26 views
8

Tôi hiện đang có mã này lên và chạy:Kết hợp C++ thuật toán tiêu chuẩn bằng cách lặp chỉ một lần

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 

Tôi muốn làm cho nó nhỏ gọn hơn và có thể đọc được nó bằng cách:

  1. Soạn tiêu chuẩn hiện hành C++ thuật toán (*)
  2. Thực hiện các vòng lặp chỉ một lần

(*) tôi giả định rằng c ombining thuật toán hiện làm cho mã của tôi dễ đọc hơn ...

Một giải pháp thay thế

Ngoài việc xác định một thuật toán tùy chỉnh transform_until, theo đề nghị của jrok, nó có thể là có thể xác định một adapter tùy chỉnh iterator rằng sẽ lặp sử dụng trình lặp cơ bản nhưng xác định lại toán tử *() bằng cách sửa đổi tham chiếu cơ bản trước khi trả về nó. Một cái gì đó như thế:

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_; 
}; 

Tất nhiên điều này vẫn còn rất thô, nhưng nên đưa ra ý tưởng. Với adapter trên, tôi sau đó có thể viết như sau:

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

với những điều sau đây được xác định trước (có thể được đơn giản hóa bằng một số mẫu-magic):

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); 

Nếu tiêu chuẩn sẽ bao gồm một bộ chuyển đổi như vậy tôi sẽ sử dụng nó, bởi vì nó có nghĩa là nó hoàn hảo, nhưng vì đây không phải là trường hợp tôi có lẽ sẽ giữ cho vòng lặp của tôi như vậy.

Như một bộ chuyển đổi sẽ cho phép tái sử dụng các thuật toán hiện hành và trộn chúng theo nhiều cách khác nhau không thể ngày hôm nay, nhưng nó có thể có nhược điểm là tốt, mà tôi có thể nhìn vào lúc này ...

+0

Trong mã hiện tại của tôi, có một vòng lặp đơn. Quan điểm của tôi là sau khi viết lại sẽ vẫn có một vòng lặp đơn. –

+1

Cuộc di cư sớm dựa trên '! Isalpha (* it)' là điều duy nhất tôi thấy có khả năng ngăn cản bạn đạt được những gì tôi nghĩ bạn đang tìm kiếm, và thành thật, bất cứ điều gì có thể làm điều đó cho bạn (và tôi không thể thấy bất cứ điều gì ngay lập tức mà sẽ) có khả năng sẽ được như vậy phức tạp bạn rõ ràng-yếu tố sẽ đi ra ngoài cửa sổ. Tôi có thể gắn bó với những gì bạn có. – WhozCraig

+0

Cảm ơn tất cả vì những câu trả lời đầy cảm hứng của bạn. Tôi sẽ gắn bó với mã hiện tại của tôi bây giờ. Tôi tin rằng 'boost :: transform_iterator' là điều gần nhất tôi đang tìm kiếm. Trường hợp sử dụng này sẽ được bao phủ bởi một bộ điều hợp vòng lặp "tác dụng phụ", một cái gì đó sẽ được sử dụng như thế này: 'word.erase (std :: find_if (sidefx_iterator (word.begin(), :: toupper), word.end (), std :: not1 (:: isalpha)), word.end()); ' –

Trả lời

9

Tôi không nghĩ rằng có một cách sạch sẽ để làm điều này với một thuật toán chuẩn duy nhất.Không có gì mà tôi biết có một biến vị ngữ (bạn cần một cái để quyết định khi nào phá vỡ sớm) và cho phép sửa đổi các phần tử của chuỗi nguồn.

Bạn có thể viết thuật toán chung của riêng mình nếu bạn thực sự muốn thực hiện theo cách "chuẩn". Hãy gọi nó là, hmm, transform_until:

#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 << '.'; 
} 

Đó là gây tranh cãi cho dù đây là bất kỳ tốt hơn so với những gì bạn có :) Đôi khi một đồng bằng vòng lặp for là tốt nhất.

+5

+1, Điều này là tốt như tôi nghĩ OP sẽ nhận được. Trong khi anh ta có thể gắn bó với kế hoạch A và chỉ giữ vòng lặp của mình, câu trả lời này là một khái niệm khá tốt để viết một máy biến áp container tùy chỉnh, tên thích hợp, và vẫn trả lời câu hỏi của OP. Ngay cả khi OP không sử dụng nó, vẫn là một câu trả lời hay. – WhozCraig

+0

Tôi đã chấp nhận câu trả lời này bởi vì mã kết quả là ngắn và dễ đọc, và thể hiện một thuật toán chung có thể hữu ích nói chung. Tất cả các thuật toán STL hiện có có một trình lặp kết thúc cố định và yêu cầu một vòng lặp trong toàn bộ vùng chứa. Tôi nghĩ rằng đó là một nhu cầu chung để có thể có một kết thúc có điều kiện. –

0

Sau khi sự hiểu biết tốt hơn câu hỏi của bạn, tôi có ý tưởng có thể hoạt động, nhưng yêu cầu Boost.

Bạn có thể sử dụng transform_iterator gọi toupper trên tất cả các ký tự và sử dụng làm ký tự nhập liệu find_if hoặc remove_if. Tôi không quen thuộc với Boost để cung cấp một ví dụ mặc dù.

Khi @jrok chỉ ra, transform_iterator sẽ chỉ chuyển đổi giá trị trong quá trình lặp và không thực sự sửa đổi vùng chứa ban đầu. Để giải quyết vấn đề này, thay vì hoạt động trên cùng một trình tự, bạn sẽ muốn sao chép sang một chuỗi mới, sử dụng một cái gì đó như remove_copy_if. Bản sao này miễn là vị từ KHÔNG đúng, do đó, cần có std::not1. Điều này sẽ thay thế trường hợp remove_if.

Sử dụng std::copy để sao chép cho đến khi trình lặp được trả về std::find_if để yêu cầu trường hợp khác hoạt động.

Cuối cùng, nếu chuỗi đầu ra của bạn trống, nó sẽ cần loại biến lặp std::inserter cho đầu ra.

+0

Nó lặp lại hai lần - OP muốn tránh điều đó. – jrok

+0

Có vẻ như có hai vòng trong quá trình thực hiện của bạn ... Tôi có sai không? –

+0

@ MariJosé Ah tôi hiểu rõ hơn câu hỏi của bạn bây giờ, có bạn là chính xác, có 2 vòng. –

Các vấn đề liên quan