Re[5]: Помогите реализовать алгоритм
От: Кодт Россия  
Дата: 23.09.04 10:58
Оценка: 9 (3)
К>На перле это будет ровно столько строк, сколько здесь.

my $src = "a&a [A] b [B] c&c [C] d [A] e [B] f [C] g [A] h";
print "string: $src\n";

## 1
$src =~ s/\&/&/g;
print "reserve marker: $src\n";

## 2
my @words = ($src =~ /\[(.*?)\]/g);

## 3
foreach my $word (@words) {
    print "word is $word\n";
## 3.1
    $src =~ s/\[$word\]/&mark;/;
    print "...replace first: $src\n";

## 3.2
    $src =~ s/\[$word\]//g;
    print "...remove others: $src\n";

## 3.3
    $src =~ s/\&mark;/[$word]/;
    print "...restore first: $src\n";
}

## 4
$src =~ s/\&/&/g;
print "restore marker: $src\n";
Перекуём баги на фичи!
Помогите реализовать алгоритм
От: momart  
Дата: 22.09.04 13:52
Оценка:
Здравствуйте!
Есть такая задача:
строка [word1] bla-bla-bla [word2] tra-ta-ta [word1] be-be-be [word3] a-a-a [word2] и так далее. Надо оставить только первые вхождения слов в скобках:
[word1] bla-bla-bla [word2] tra-ta-ta be-be-be [word3] a-a-a
Как можно это красивей всего сделать, может использовать рег. выражения? (ограничений на исп. .NET нет), или и без рег. выражений можно быстро сделать?
Спасибо!


23.09.04 14:59: Перенесено из 'C/C++'
Re: Помогите реализовать алгоритм
От: korzhik Россия  
Дата: 22.09.04 20:38
Оценка:
Здравствуйте, momart, Вы писали:

M>Здравствуйте!

M>Есть такая задача:
M>строка [word1] bla-bla-bla [word2] tra-ta-ta [word1] be-be-be [word3] a-a-a [word2] и так далее. Надо оставить только первые вхождения слов в скобках:
M>[word1] bla-bla-bla [word2] tra-ta-ta be-be-be [word3] a-a-a

вот мой вариант:
#include <iostream>
#include <string>
#include <vector>

template<typename Iterator>
class tokenizer
{
private: 
    tokenizer(const tokenizer&);
    tokenizer& operator=(const tokenizer&);
public:
    struct token
    {
        Iterator first;
        Iterator last;
    };    

public:
    tokenizer()
    {
    }
    
    void set_str(Iterator first, Iterator last)
    {    
        first_    = first;
        last_    = last;
        current_= first_;
    }
    
    token current_token()
    {
        return token_;
    }
    
    bool next();

private:
    Iterator    first_;
    Iterator    last_;
    Iterator    current_;

    token        token_;
};

//-----------------------------------------------------------------------
template<typename Iterator>
bool tokenizer<Iterator>::next()
{
    if (current_ == last_) 
    {
        return false;
    }

    while (current_ != last_ && *current_ != '[') 
    {
        ++current_;
    }

    if (current_ == last_) 
    {
        return false;
    }

    unsigned count = 0;
    Iterator p = current_;

    while (p != last_ && *p != ']') 
    {
        ++p;
        ++count;
    }

    ++count;

    if (p == last_) 
    {
        return false;
    }

    token_.first = current_;
    token_.last  = current_ + count;

    current_ += count;

    return true;
}

//-----------------------------------------------------------------------
int main()
{
    std::string str("[word1] bla-bla-bla [word2] tra-ta-ta [word1] be-be-be [word3] a-a-a [word2]");

    typedef tokenizer<std::string::iterator> tokenizer;
    
    tokenizer tok;
    tok.set_str(str.begin(), str.end());

    std::vector<tokenizer::token> tokens;

    while (tok.next())
    {
        tokenizer::token tk = tok.current_token();

        std::vector<tokenizer::token>::iterator it   = tokens.begin();
        std::vector<tokenizer::token>::iterator last = tokens.end();

        for (;it != last; ++it)
        {
            if (std::distance(it->last, it->first) == std::distance(tk.last, tk.first) && 
                std::string(it->first, it->last) == std::string(tk.first, tk.last))
            {
                tok.set_str(str.erase(tk.first, tk.last), str.end());
            }
        }

        tokens.push_back(tok.current_token());
    }

    std::cout << str << std::endl;
}


кто будет критиковать код сразу сообщаю, что я писал его уставший и ,честно говоря, немного выпивший, так что особо не ругайте.
Re[2]: Помогите реализовать алгоритм
От: korzhik Россия  
Дата: 23.09.04 09:20
Оценка:
Здравствуйте, korzhik, Вы писали:

маленькие исправления:
    while (tok.next())
    {
      bool bad_token = false;
        tokenizer::token tk = tok.current_token();

        std::vector<tokenizer::token>::iterator it   = tokens.begin();
        std::vector<tokenizer::token>::iterator last = tokens.end();

        for (;it != last; ++it)
        {
            if (std::distance(it->last, it->first) == std::distance(tk.last, tk.first) && 
                std::string(it->first, it->last) == std::string(tk.first, tk.last))
            {
                tok.set_str(str.erase(tk.first, tk.last), str.end());
                bad_token = true;
                break;
            }
        }

        if (!bad_token)
            tokens.push_back(tok.current_token());
    }
Re[3]: Помогите реализовать алгоритм
От: momart  
Дата: 23.09.04 10:14
Оценка:
Здравствуйте, korzhik.

Спасибо за помощь . Ваша реализация чудесно работает. Хотя, мне все же интересно (из чистого любопытства) как можно это сделать с помощью регулярных выражений (мне кажется это будет пара строчек кода).
Re[4]: Помогите реализовать алгоритм
От: Кодт Россия  
Дата: 23.09.04 10:48
Оценка:
Здравствуйте, momart, Вы писали:

M>Спасибо за помощь . Ваша реализация чудесно работает. Хотя, мне все же интересно (из чистого любопытства) как можно это сделать с помощью регулярных выражений (мне кажется это будет пара строчек кода).


Ну не пара...

1) эскейпить префикс маркера: /&/&amp;/g
2) Получить с помощью регекспа массив всех слов: /\[(.*?)\]/g
3) для каждого слова $word из массива выполнить замену:
3.1) заменить первое вхождение на маркер /\[$word\]/&marker;/
3.2) заменить все (оставшиеся) на пусто /\[$word\]//g
3.3) заменить маркер на значение /\&marker;/[$word]/
4) разэскейпить префикс: /\&amp;/&/g

На перле это будет ровно столько строк, сколько здесь.
Перекуём баги на фичи!
Re[2]: Помогите реализовать алгоритм
От: investigator Россия  
Дата: 23.09.04 12:23
Оценка:
Здравствуйте, korzhik, Вы писали:

K>Здравствуйте, momart, Вы писали:


M>>Здравствуйте!

M>>Есть такая задача:
M>>строка [word1] bla-bla-bla [word2] tra-ta-ta [word1] be-be-be [word3] a-a-a [word2] и так далее. Надо оставить только первые вхождения слов в скобках:
M>>[word1] bla-bla-bla [word2] tra-ta-ta be-be-be [word3] a-a-a

K>вот мой вариант:

K>
K>#include <iostream>
K>#include <string>
K>#include <vector>

K>template<typename Iterator>
K>class tokenizer
K>{
K>private: 
K>    tokenizer(const tokenizer&);
K>    tokenizer& operator=(const tokenizer&);
K>public:
K>    struct token
K>    {
K>        Iterator first;
K>        Iterator last;
K>    };    

K>public:
K>    tokenizer()
K>    {
K>    }
    
K>    void set_str(Iterator first, Iterator last)
K>    {    
K>        first_    = first;
K>        last_    = last;
K>        current_= first_;
K>    }
    
K>    token current_token()
K>    {
K>        return token_;
K>    }
    
K>    bool next();

K>private:
K>    Iterator    first_;
K>    Iterator    last_;
K>    Iterator    current_;

K>    token        token_;
K>};

K>//-----------------------------------------------------------------------
K>template<typename Iterator>
K>bool tokenizer<Iterator>::next()
K>{
K>    if (current_ == last_) 
K>    {
K>        return false;
K>    }

K>    while (current_ != last_ && *current_ != '[') 
K>    {
K>        ++current_;
K>    }

K>    if (current_ == last_) 
K>    {
K>        return false;
K>    }

K>    unsigned count = 0;
K>    Iterator p = current_;

K>    while (p != last_ && *p != ']') 
K>    {
K>        ++p;
K>        ++count;
K>    }

K>    ++count;

K>    if (p == last_) 
K>    {
K>        return false;
K>    }

K>    token_.first = current_;
K>    token_.last  = current_ + count;

K>    current_ += count;

K>    return true;
K>}

K>//-----------------------------------------------------------------------
K>int main()
K>{
K>    std::string str("[word1] bla-bla-bla [word2] tra-ta-ta [word1] be-be-be [word3] a-a-a [word2]");

K>    typedef tokenizer<std::string::iterator> tokenizer;
    
K>    tokenizer tok;
K>    tok.set_str(str.begin(), str.end());

K>    std::vector<tokenizer::token> tokens;

K>    while (tok.next())
K>    {
K>        tokenizer::token tk = tok.current_token();

K>        std::vector<tokenizer::token>::iterator it   = tokens.begin();
K>        std::vector<tokenizer::token>::iterator last = tokens.end();

K>        for (;it != last; ++it)
K>        {
K>            if (std::distance(it->last, it->first) == std::distance(tk.last, tk.first) && 
K>                std::string(it->first, it->last) == std::string(tk.first, tk.last))
K>            {
K>                tok.set_str(str.erase(tk.first, tk.last), str.end());
K>            }
K>        }

K>        tokens.push_back(tok.current_token());
K>    }

K>    std::cout << str << std::endl;
K>}
K>


K>кто будет критиковать код сразу сообщаю, что я писал его уставший и ,честно говоря, немного выпивший, так что особо не ругайте.


И все-таки покритикуем. Правда не код, а алгоритм. Здесь все хорошо за исключением квадратичности. Лучше вместо внутреннего цикла использовать HashSet для проверки того, что такой тоукен уже встречался. Алгоритм будет работать в N раз быстрее (где N-количество слов в []).

Можно подумать и о том, чтобы заменить удаление во внутреннем цикле на добавление к результирующей строке. В крайнем случае сейчас мы произведем N-1 удаление, при этом всего произойдет N-1 копирование ВСЕЙ оставшейса строки.
Re[3]: Помогите реализовать алгоритм
От: korzhik Россия  
Дата: 23.09.04 13:33
Оценка:
Здравствуйте, investigator, Вы писали:

I>И все-таки покритикуем.

люблю конструктивную критику

>Лучше вместо внутреннего цикла использовать HashSet для проверки того, что такой тоукен уже встречался.

согласен
Re[3]: Помогите реализовать алгоритм
От: korzhik Россия  
Дата: 23.09.04 15:35
Оценка:
Здравствуйте, investigator, Вы писали:

I>Можно подумать и о том, чтобы заменить удаление во внутреннем цикле на добавление к результирующей строке.

это хорошее предложение
std::string kill_dup(const std::string& source)
{
    typedef tokenizer<std::string::const_iterator> tokenizer;

    std::string::const_iterator start = source.begin();
    std::vector<tokenizer::token> tokens;
    std::string result;
    tokenizer tok(source.begin(), source.end());

    while (tok.next())
    {
        typedef std::vector<tokenizer::token>::iterator iterator; 
        iterator it = tokens.begin();
        iterator last = tokens.end();
        
        tokenizer::token tk = tok.current_token();

        while ( it != last && 
                !(std::distance(it->last, it->first) == std::distance(tk.last, tk.first) &&
                 std::string(it->first, it->last) == std::string(tk.first, tk.last)))
        {
            ++it;
        }
        if (it != last)
        {
            result.append(start, tk.first);
            start = ++tk.last;
        }
        else
        {
            tokens.push_back(tk);
        }
    }
    return result;
}

//-----------------------------------------------------------------------
int main()
{
    std::cout 
        << kill_dup("[word1] bla-bla-bla [word2] tra-ta-ta [word1] be-be-be [word3] a-a-a [word2]") 
        << std::endl;
}
Re[2]: Помогите реализовать алгоритм
От: korzhik Россия  
Дата: 23.09.04 18:25
Оценка:
Здравствуйте, korzhik, Вы писали:
K>вот мой вариант:

ну в общем вот последний вариант.
не много порефакторил, исправил баги.
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

//-----------------------------------------------------------------------
template<typename Iterator>
class tokenizer
{
private: 
    tokenizer(const tokenizer&);
    tokenizer& operator=(const tokenizer&);
public:
    struct token { Iterator first, last; };    

    tokenizer(Iterator first, Iterator last) : current_(first), last_(last){}

    token current_token() const { return token_;}
    
    bool next()
    {
        return last_ == (current_ = std::find(current_, last_, '[')) ? 
            false : 
            last_ != (current_ = token_.last = std::find(token_.first = current_, last_, ']'));
    }

private:
    const Iterator    last_;
    Iterator        current_;
    token            token_;
};

namespace 
{
    class tokencmp
    {
        typedef tokenizer<std::string::const_iterator> tokenizer;
        tokenizer::token tk_;
    public:
        tokencmp(const tokenizer::token& tk) : tk_(tk) {}

        bool operator()(const tokenizer::token& tk) const
        {
            return std::string(tk.first, tk.last) == std::string(tk_.first, tk_.last);
        }
    };
} // unnamed namespace
//-----------------------------------------------------------------------
std::string kill_dup(const std::string& source)
{
    typedef tokenizer<std::string::const_iterator> tokenizer;

    std::string::const_iterator start = source.begin();
    std::vector<tokenizer::token> tokens;
    std::string result;
    tokenizer tok(source.begin(), source.end());

    while (tok.next())
    {
        tokenizer::token tk = tok.current_token();

        if (tokens.end() != std::find_if(tokens.begin(), tokens.end(), tokencmp(tk)))
        {
            result.append(start, tk.first);
            start = ++tk.last;
        }
        else
        {
            tokens.push_back(tk);
        }
    }
    return result;
}

//-----------------------------------------------------------------------
int main()
{
    std::cout 
        << kill_dup("[word1] bla-bla-bla [word2] tra-ta-ta [word1] be-be-be [word3] a-a-a [word2]") 
        << std::endl;
}
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.