Поиск подинтервалов в списке
От: placement_new  
Дата: 16.12.09 09:19
Оценка:
Есть список некоторых обьектов.
В нем последовательно ищутся подинтервалы вызовом std::search и заменяются на новые. Элементы нового подинтервала не должны принимать участие в последующих поисках std::search, то есть вставленные элементы как бы заранее не равны, тем элементам которые будут искаться. Как бы такое реализовать?
Надеюсь более менее понятно обьяснил.
Re: Поиск подинтервалов в списке
От: placement_new  
Дата: 16.12.09 09:31
Оценка:
Здравствуйте, placement_new, Вы писали:

Была мысль сохранять начало и конец вновь вставленного интервала, и написать свой search, где в предикат передавать итераторы не разименовывая и там сравнивать. Но у листа итераторы не поддреживают <, >
Re[2]: Поиск подинтервалов в списке
От: placement_new  
Дата: 16.12.09 09:34
Оценка:
Здравствуйте, placement_new, Вы писали:

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


_>Была мысль сохранять начало и конец вновь вставленного интервала, и написать свой search, где в предикат передавать итераторы не разименовывая и там сравнивать. Но у листа итераторы не поддреживают <, >

Можно, еще сохранять всю последовательность, а не только начала и конец, но мне кажется это не православным, такой алгоритм.
Re: Поиск подинтервалов в списке
От: Кодт Россия  
Дата: 16.12.09 16:09
Оценка: 3 (1)
Здравствуйте, placement_new, Вы писали:

_>Есть список некоторых обьектов.

_>В нем последовательно ищутся подинтервалы вызовом std::search и заменяются на новые. Элементы нового подинтервала не должны принимать участие в последующих поисках std::search, то есть вставленные элементы как бы заранее не равны, тем элементам которые будут искаться. Как бы такое реализовать?

Фактически, первый же найденный и заменённый интервал разрывает список — поскольку следующий поиск не может пересечься с заменённым куском, и будет либо строго до, либо строго после.
После второй замены — список будет разорван уже на три части... Ну и так далее.

Отсюда идея: пусть на входе будет последовательность интервалов, и на выходе тоже последовательность интервалов.

Демострашилка
#include <list>
#include <string>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <cstring>

template<class It> struct range
{
    typedef It iterator;
    It begin, end;
    range() {}
    range(It b, It e) : begin(b), end(e) {}
    bool empty() const { return begin==end; }
    ptrdiff_t size() const { return distance(begin,end); }
};
template<class It> range<It> make_range(It b, It e) { return range<It>(b,e); }

template<class R> struct found // результат search
{
    R before, here, after;
    bool failed() const { return here.empty() && after.empty(); }
};

// для наглядности, будем потрошить строку...
typedef char E;
typedef std::list<E> data_t; // список символов

// задача выражена в виде двух произвольных интервалов: что на что меняем
typedef std::string::const_iterator ToFind, ToReplace; // интервалы берём из строки
typedef std::pair< range<ToFind>, range<ToReplace> > task_t;

typedef range<data_t::iterator> chunk_t;
typedef std::list<chunk_t> chunks_t; // list принципиально - иначе процедура замены немного усложнится (из-за инвалидации при вставке-удалении)


task_t make_task(std::string const& f, std::string const& r)
{
    return task_t(make_range(f.begin(), f.end()), make_range(r.begin(), r.end()));
}

// немножко вывода
std::ostream& operator << (std::ostream& ost, chunk_t const& chunk)
{
    ost << "<";
    std::copy(chunk.begin, chunk.end, std::ostream_iterator<char>(ost, ""));
    ost << ">";
    return ost;
}
std::ostream& operator << (std::ostream& ost, chunks_t const& chunks)
{
    std::copy(chunks.begin(), chunks.end(), std::ostream_iterator<chunk_t>(ost, ""));
    return ost;
}
std::ostream& operator << (std::ostream& ost, data_t const& data)
{
    ost << "'";
    std::copy(data.begin(), data.end(), std::ostream_iterator<char>(ost, ""));
    ost << "'";
    return ost;
}


// процедура массовой замены - должна знать и сам контейнер, и коллекцию кусочков
void replace(data_t& data, chunks_t& chunks, task_t const& task)
{
    if(task.first.empty()) return;

    std::cout << data << " as " << chunks << " ~ " << (&*task.first.begin) << " / " << (&*task.second.begin) << std::endl;

    chunks_t::iterator ci = chunks.begin();
    while(ci != chunks.end())
    {
        chunk_t& chunk = *ci;

        std::cout << chunk << "..." << std::endl;
        
        data_t::iterator di = std::search(chunk.begin, chunk.end, task.first.begin, task.first.end);
        if(di == chunk.end) // не найдено
        {
            std::cout << " skipped" << std::endl;
            ++ci;
            continue;
        }
        data_t::iterator dj = di; std::advance(dj, task.first.size());
        
        ptrdiff_t headsize = std::distance(chunk.begin, di);
        
        // выполняем замену: добавляем новое, удаляем старое
        std::cout << "0: " << data << std::endl;
        data.insert(di, task.second.begin, task.second.end); // di по-прежнему указывает на искомую серию
        std::cout << "I: " << data << std::endl;
        data.erase(di, dj); // при этом инвалидируется di - придётся отсчитывать заново от chunk.begin
        std::cout << "E: " << data << std::endl;

        std::cout << " splitted to ";
    
        // добавляем головной кусок
        if(headsize != 0) // в противном случае, кстати, chunk.begin тоже инвалидирован
        {
            di = chunk.begin; std::advance(di, headsize);
            chunk_t newchunk(chunk.begin, di);
            chunks.insert(ci, newchunk);
            std::cout << newchunk;
        }
        else
        {
            std::cout << "nothing";
        }
        
        std::cout << " and ";
        
        // добавляем (переписываем) хвостовой кусок
        if(dj == chunk.end)
        {
            std::cout << "nothing";
            chunks.erase(ci++); // хвост пуст - удаляем (и продолжим забег уже со следующего куска)
        }
        else
        {
            chunk.begin = dj; // хвост не пуст - подправляем (и продолжим забег по данному куску)
            std::cout << chunk;
        }
        
        std::cout << std::endl;
        
        // и теперь, в зависимости от твоей задачи, либо продолжаем забег с данным task, либо завершаем работу
    }
    
    std::cout << data << " as " << chunks << std::endl << "---------------" << std::endl;
}

int main()
{
    std::string source = "mama myla ramu, rama krasnel so sramu";

    data_t data(source.begin(), source.end());
    chunks_t chunks(1, chunk_t(data.begin(),data.end()));

    // строки заданий здесь создаются временные - но они живут до конца выполнения replace
    replace(data, chunks, make_task("ma", "panama"));
    replace(data, chunks, make_task("am", "abram"));
    replace(data, chunks, make_task("m", "z"));
    
    // а вот так можно/нужно разнести задания и исполнение
    
    std::vector< std::pair<std::string, std::string> > ts;
    ts.push_back( std::make_pair("ma", "panama") );
    
    for(int i=0, n=ts.size(); i!=n; ++i)
        replace(data, chunks, make_task(ts[i].first, ts[i].second));
}
... << RSDN@Home 1.2.0 alpha 4 rev. 1237>>
Перекуём баги на фичи!
Re: Поиск подинтервалов в списке
От: guyos Россия  
Дата: 18.12.09 18:26
Оценка:
Здравствуйте, placement_new, Вы писали:

_>Есть список некоторых обьектов.

_>В нем последовательно ищутся подинтервалы вызовом std::search и заменяются на новые. Элементы нового подинтервала не должны принимать участие в последующих поисках std::search, то есть вставленные элементы как бы заранее не равны, тем элементам которые будут искаться. Как бы такое реализовать?
_>Надеюсь более менее понятно обьяснил.

В общем, реализация основана на обертке класса SearchObject вокруг std::search. Внутри класса запоминаютя интервалы по которым производился поиск, и потом они благополучно избегаются при следующем вызове SearchObject.search. Причем специфика реализации такова, что созданный объект класса SearchObject жестко привязан к контейнеру по которому идет поиск. Применение Object.search() к другому контейнеру приведет к ошибке, зато те контейнеры которые задают прототип поиска могут быть любые. Для каждого экземпляра контейнера нужно создавать новый объект поиска SearchObject. Для использования SearchObject::search(..., unsigned int interval) в отличие от std::search :

template<class ForwardIterator1, class ForwardIterator2>
ForwardIterator1 search(
ForwardIterator1 _First1,
ForwardIterator1 _Last1,
ForwardIterator2 _First2,
ForwardIterator2 _Last2
); , нужно указать дополнительный параметр intеrval = Last2 — First2, так как оператор "-" не определен для некоторых контейнеров (std::list<> например) и нельзя тогда было сравнивать подинтервалы. В остальном у них синтаксис совпадает. Так как использубтся шаблоны, то итераторы могут быть любые( в том числе простые указатели), главно чтобы операции ++ и * применялись к итератору.

Еще мысль есть написать свой итератор для контейнера по которому идет поиск, так чтобы он как-то взаимодействовал с std::search. То есть итератор должен как-то запоминать те куски, которые std::search нашел. Ну это так, мысли в слух.

Итак, ...


template<class ForwardIterator>
class Pair{
public:
explicit Pair(ForwardIterator f, int s):from_(f), size_(s){}
ForwardIterator from_;
unsigned int size_;
};

template<class ForwardIterator>
bool CompPair(Pair<ForwardIterator> p1, Pair<ForwardIterator> p2)
{
return (p1.from_ < p2.from_);
}

template<class ForwardIterator1, class ForwardIterator2>
class SearchObject{
int searchCount_;
ForwardIterator1 begin_, end_;
vector< Pair<ForwardIterator1> > pairVector;
SearchObject(const SearchObject& sO){}
public:
//searching for a prototype
SearchObject(): searchCount_(0), begin_(), end_(){}
ForwardIterator1 search(
ForwardIterator1, ForwardIterator1,
ForwardIterator2, ForwardIterator2, unsigned int);
};

/* interval is a number of objects in search prototype ( = Last2 — First2)*/
template <class ForwardIterator1, class ForwardIterator2>
ForwardIterator1 SearchObject<ForwardIterator1, ForwardIterator2>::search(
ForwardIterator1 First1, ForwardIterator1 Last1,
ForwardIterator2 First2, ForwardIterator2 Last2, unsigned int interval)
{
ForwardIterator1 found;
if(!searchCount_){
found = std::search(First1, Last1, First2, Last2);
if(found != Last1){
begin_ = First1;
end_ = Last1;
pairVector.push_back(Pair<ForwardIterator1>(found, interval));
searchCount_++;
}
return found;
}

if(begin_ > First1)
begin_ = First1;
if(end_ < Last1)
end_ = Last1;

if(pairVector.begin()->from_ — begin_ >= interval){
found = std::search(begin_, pairVector.begin()->from_, First2, Last2);
if(found != pairVector.begin()->from_){
pairVector.push_back(Pair<ForwardIterator1>(found, interval));
sort(pairVector.begin(), pairVector.end(), CompPair<ForwardIterator1>);
return found;
}
}

for( unsigned int i = 0; i <= pairVector.size()-2; i++ ){
if(pairVector[i+1].from_ — pairVector[i].from_ — pairVector[i].size_ >= interval)
found = std::search(pairVector[i].from_ + pairVector[i].size_,
pairVector[i+1].from_, First2, Last2);
if(found != pairVector[i+1].from_){
pairVector.push_back(Pair<ForwardIterator1>(found, interval));
sort(pairVector.begin(), pairVector.end(), CompPair<ForwardIterator1>);
return found;
}
}

if(end_ — pairVector.end()->from_ — pairVector.end()->size_ >= interval){
found = std::search(pairVector.end()->from_ + pairVector.end()->size_,
end_, First2, Last2);
if(found != end_){
pairVector.push_back(Pair<ForwardIterator1>(found, interval));
sort(pairVector.begin(), pairVector.end(), CompPair<ForwardIterator1>);
return found;
}
}

return Last1;
}

typedef vector<int>::iterator VIter;
typedef list<int>::iterator LIter;

int main()
{
vector<int> v;
list<int> L1, L2;

int i;
for(i = 1; i <= 20; i++)
{
v.push_back(i);
}
for(i = 7; i <= 15; i++)
{
L1.push_back(i);
}
for(i = 9; i <= 19; i++)
{
L2.push_back(i);
}

VIter result;
SearchObject<VIter, LIter> Ob;

result = Ob.search(v.begin(), v.end(), L1.begin(), L1.end(), 6);
PrintResult(result, v, L1);

result = Ob.search(v.begin(), v.end(), L2.begin(), L2.end(), 4);
PrintResult(result, v, L2);

return 0;
}
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.