Модераторы: korob2001, ginnie
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> удаление из массива 
:(
    Опции темы
oldlamer
Дата 14.8.2006, 17:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 5
Регистрация: 14.8.2006

Репутация: нет
Всего: нет



вот эта строчка возвращает массив из пяти строк (каждая строка помоему ссылка на хэш ) 

Код

$out->{sources} = [map {$_->to_hash} $self->{manager}->sources->get_sources($operator->cur_locale)];



ф-ция to_hash выглядит так:

Код


sub to_hash {
    my $self = shift;
    my $hash = {
        code    =>    $self->code,
        name    =>    $self->name,
        flag    =>    $self->flag,
    };
    return $hash;
}


вопрос: как удалить одну из строк полностью из этого массива?
условие: это значение ключа code одного из хешей


PM MAIL   Вверх
korob2001
Дата 14.8.2006, 23:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2871
Регистрация: 29.12.2002

Репутация: 13
Всего: 61



Вот написал небольшой пример:
Код

#!/usr/bin/perl -w
use strict;

# Создаём массив, ссылок на хеш
my @arr = ( { code => 2, name => "Two", flag => "false" },
            { code => 3, name => "Three", flag => "false" },
            { code => 4, name => "Four", flag => "true" },
            { code => 4, name => "Four", flag => "true" },
            { code => 1, name => "One", flag => "true" },
            { code => 3, name => "Three", flag => "false" },
            { code => 5, name => "Five", flag => "false" },
            { code => 3, name => "Three", flag => "false" }, );

# Подпрограмма получает ссылку на массив и коды, которые нужно удалить из массива.
# Возвращает новый массив или ссылку на массив, в зависимости от контекста
sub rmCodes {
    my($array_ref, @codes) = @_;
    my($flag, @new_array)  = ();
    foreach my $hash_ref (@$array_ref) {
            $flag = 0;
            foreach my $code (@codes) {
                  $flag = 1 if $code == $hash_ref->{code};
            }
            push @new_array, $hash_ref unless $flag;
    }
    return wantarray ? @new_array : \@new_array;
}

# Удаляем из массива не нужные елементы, код которых передаём в качестве аргументов
@arr = rmCodes(\@arr, 4, 5, 3);

# Покажем текуший массив
foreach my $hash_ref (@arr) {
    print $hash_ref->{code} . "\n";
    print $hash_ref->{name} . "\n";
    print $hash_ref->{flag} . "\n" . "=" x 79 . "\n";
}

Но в данном случае у нас происходят некоторые накладные расходы, так как на каждой итерации внешнего цикла, мы проходим по всему массиву @codes во внутреннем цикле, даже по тем элементам, которые уже были удалены. Хорошо если этих элементов несколько, а если их будет несколько сотен тысячь? Так же у нас есть переменная $flag, которая в принципе нам и не нужна, да и копия массива, который может быть не понятно какого размера.
Давай попробуем переписать наш код так, что бы избавляться динамически от тех елементов массива @codes, которые уже были удалены, так же попробуем избавиться от переменной $flag и от лишней копии массива, раз уж мы передаём аргумент функции по ссылке:
Код

#!/usr/bin/perl -w
use strict;

# Создаём массив, ссылок на хеш
my @arr = ( { code => 2, name => "Two", flag => "false" },
            { code => 3, name => "Three", flag => "false" },
            { code => 4, name => "Four", flag => "true" },
            { code => 4, name => "Four", flag => "true" },
            { code => 1, name => "One", flag => "true" },
            { code => 3, name => "Three", flag => "false" },
            { code => 5, name => "Five", flag => "false" },
            { code => 3, name => "Three", flag => "false" }, );

# Подпрограмма получает ссылку на массив и коды, которые нужно удалить из массива.
sub rmCodes {
    my($array_ref, @codes) = @_;
    while (my $code = shift @codes) {
           for (my $pos=0; $pos < @$array_ref; $pos++) {
                splice(@$array_ref, $pos--, 1) if ($$array_ref[$pos]->{code} == $code);
           }
    }
}

# Удаляем из массива не нужные елементы, код которых передаём в качестве аргументов
rmCodes(\@arr, 4, 3);

# Покажем текуший массив
foreach my $hash_ref (@arr) {
    print $hash_ref->{code} . "\n";
    print $hash_ref->{name} . "\n";
    print $hash_ref->{flag} . "\n" . "=" x 79 . "\n";
}

Заметь, что циклы поменялись местами, так же теперь мы используем цикл while, вместо foreach. Так же обрати внимание на то, как вызывается функция и на то, что она не возвращает никакого значения (void). Она работает на прямую с массивом, ссылку на который получила в качестве первого аргумента. Второй пример будет работать на много быстрее и не будет так лихо расходовать ресурсы (память), единственное не забудь сделать копию, этого массива (если она нужна), до вызова функции rmCodes().



Это сообщение отредактировал(а) korob2001 - 14.8.2006, 23:56


--------------------
"Время проходит", - привыкли говорить вы по неверному пониманию. 
"Время стоит - проходите вы".
PM MAIL WWW ICQ MSN   Вверх
oldlamer
Дата 15.8.2006, 09:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 5
Регистрация: 14.8.2006

Репутация: нет
Всего: нет



Спасибо за советы, тем более с примерами
я наваял код очень похожий на Ваш первый вариант с применением временного массива:

Код


    my $temparr = [map {$_->to_hash} $self->{manager}->sources->get_sources($operator->cur_locale)];

        foreach my $s (@$temparr) {
            if($s->{code} ne '2') {
            push(@{$out->{sources}}, $s);
                    }
            }



вопрос :
а нельзя ли отфильтровать моссив на момент его формирования?
ведь в перле практически всё возможно...

Код

$out->{sources} = [ #"здесь?" map {$_->to_hash} $self->{manager}->sources->get_sources($operator->cur_locale)];


PM MAIL   Вверх
korob2001
Дата 17.8.2006, 03:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2871
Регистрация: 29.12.2002

Репутация: 13
Всего: 61



Цитата

а нельзя ли отфильтровать моссив на момент его формирования?

Ну значит вставь код, который будет отфильтровывать, туда где формируется массив. Насколько я понял то вот здесь: $self->{manager}->sources->get_sources().
Хотя, коли это ООП, то помоему лучше всего будет создать класс, который будет наследовать от производного класса и добавить в него несколько новых методов или переопределить уже имеющиеся. Помоему это самое то, так как ты хочешь расширить возможности уже имеющегося класса.
Можно было бы конечно залезть внутрь класса, который уже есть и править код в нём, но это помоему нарушит идею ООП вцелом.

Вот тебе повод для размышления:
Давай напишем класс, к примеру такой:
Код

package Source;

sub new {
    my $check = shift;
    my $class = ref( $check ) || $check;
    my $self  = { source  => "source.txt",
                  current => 0, @_ };
    bless($self, $class);
    $self->_initialize();
    return $self;
}

sub _initialize {
    my $self = shift;
    if (-e $self->{source}) {
         open my $fh, "<", $self->{source} or die "Can't open file '$self->{source}': $!";
         while (my $line = <$fh>) {
                chomp($line);
                my($code,$name,$flag) = split(/\:/,$line);
                push(@{$self->{data}}, {code => $code, name => $name, flag => $flag});
         }
         close $fh;
    }
}

sub add {
    my $self = shift;
    my($code,$name,$flag) = @_;
    push(@{$self->{data}}, {code => $code, name => $name, flag => $flag});
}

sub getList {
    my $self = shift;
    if ( @{$self->{data}} ) {
         return wantarray ? @{$self->{data}} : $self->{data};
    }
    return ();
}

sub getCurrent {
    my $self = shift;
    if ($self->{current} < 0) {
         return undef;
    } else {
         return $self->{data}[$self->{current}] || undef;
    }
}

sub toNext {
    ++shift->{current};
}

sub toPrevious {
    --shift->{current};
}

sub save {
    my $self = shift;
    open my $fh, ">", $self->{source} or die "Can't open file '$self->{source}': $!";
         foreach my $hash_ref (@{$self->{data}}) {
                 print $fh join(":", $hash_ref->{code}, $hash_ref->{name}, $hash_ref->{flag}) . "\n";
         }
    close($fh) or die "Can't close file '$self->{source}': $!";
}

sub toLast {
    my $self = shift;
    $self->{current} = @{$self->{data}} - 1;
}

sub toFirst {
    shift->{current} = 0;
}

1;

Сохрани его в фале Source.pm

Немного опишу, что он делает:
1. Когда ты создаёшь объект этого класса, то он тут же проверяет, существует ли файл который был передан как параметр source в конструкторе. Если да, то он открывает этот файл и читает его в память. Предполагается, что структура файла следующая:
Код

code:name:flag
code:name:flag
code:name:flag
и т.д.

Но не торопись сразу создавать этот файл в ручную, так как наш класс тоже в состоянии это сделать. Далее идут методы, вот их краткое описание:

_initialize() - закрытый метод инициализации, именно здесь открывается файл и считывается информация из него в память. ( Закрытый, это не значит, что у тебя к нему нет доступа, т.е. не нужно его буквально воспринимать как закрытый (private), просто трогать его не нужно ). Потому его имя на чинается с символа подчёркивания _, по неписанному соглашению.

add() - метод получает три параметра (code,name,flag) и формирует на их основе анонимный хеш { code => code, name => name, flag => flag } и добавляет его в конец массива.

getList() - метод возвращает либо массив хешей, либо ссылку на массив хешей, в зависимости от контекста. (Собственно это тот метод, который нас не оченьто устраивает).

getCurrent() - метод возравращает текущий елемент, на котором в данный момент расположен бегунок.

toNext() - метод передвигает бегунок в следующую позицию.

toPrevious() - метод передвигает бегунок в предидущую позицию.

toLast() - метод передвигает бегунок на последнюю позицию.

toFirst() - метод передвигает бегунок на первую позицию.

save() - текущий массив хешей из памяти записывается в файл.

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

Теперь давай попробуем наш класс в работе, да бы убедиться в его работоспособности, заодно и создадим наш файл.

Сохрани следующий код в тот же каталог, куда сохранил файл Source.pm, назвать его можешь как хочешь, лишь бы расширение было как у исполняемого файла, например: .pl.
Код

#!/usr/bin/perl -w
use strict;
use Source;

my $source = new Source(source => "source.txt");

# Установим бегунок в начало
$source->toFirst();

# Если не опредёлён текуший елемент, значит довам несколько новых
unless ($source->getCurrent()) {
        $source->add(1,"One","true");
        $source->add(2,"Two","false");
        $source->add(5,"Five","true");
        $source->add(5,"Second five", "false");
        $source->add(5,"Third five", "false");
        $source->add(3,"Three","true");
        $source->add(4,"Four","false");
}

# Выводим все элементы в цикле
while (my $current = $source->getCurrent()) {
       print $current->{code} . "\n";
       print $current->{name} . "\n";
       print $current->{flag} . "\n";
       print "=" x 79, "\n";
       # Перемещаем бегунок в следующую позицию
       $source->toNext();
}

# Добавим ещё один елемент
$source->add(6,"Six","true");


print "\n\n\n";


# Установим текущую позицию "бегунка" в конец
$source->toLast();

# Покажем все елементы в обратном порядке
while (my $current = $source->getCurrent()) {
       print $current->{code} . "\n";
       print $current->{name} . "\n";
       print $current->{flag} . "\n";
       print "=" x 79, "\n";
       # Перемещаем бегунок в предидущую позицию
       $source->toPrevious();
}

# Сохраним изменения
$source->save();

Только запусти его 1 раз, так как если ты сделаешь это больше одного раза, то при каждом запуске будет добавляться елемен Six в конец файла. Но можешь после первого запуска закоментировать строку $source->save(), тогда все изменения не будут сохраняться в файл.
После первого запуска, в том же каталоге, будет создан файл source.txt в нём и будет храниться наш массив.

Всё это хорошо, но нам нужно было другое, нам необходимо испытать работу метода getList(). Сохрани в тот же каталог, такой код в файл с любым именем, с раширением .pl.
Код

#!/usr/bin/perl -w
use strict;
use Source;

my $source = new Source( source => "source.txt" );

foreach my $current ($source->getList()) {
        print $current->{code} . "\n";
        print $current->{name} . "\n";
        print $current->{flag} . "\n";
        print "=" x 79 . "\n";
}

Как видишь, мы получили на STDOUT именно ту структуру, которая находится в файле source.txt, сделали это с помощью метода getList().
Но нас это не очень устраивает, так как нам необходимо избавляться от тех елементов, код которых нас не интересует. Можно было бы залезть в класс Source.pm и править его напрямую, но в таком случае зачем нам использовать ООП? По сути одна из прелестей ООП - это раширение возможностей  без изменения уже существующего кода.

Давай попробуем разобраться, что же нам нужно? Итак у нас есть класс, который вполне работоспособный. В нём есть несколько методов, которые мы хотели бы иметь в своём распоряжении и плюс добавить к ним несколько методов или переопределить уже существующие. Помоему наследование так и напрашивается. Давай попробуем создать производный каласс NewSource.pm от нашего класса Source.pm. Вот код:
Код

package NewSource;
require Source;

@ISA = ("Source"); # Наследуем от класса Source

# Определяем новый метод
sub getListWithoutCodes {
    my $self = shift;
    my @result = ();
    foreach my $current ( $self->getList() ) {
           my $flag = 0;
           foreach my $code (@_) {
                   $flag = 1 if ($code == $current->{code});
           }
           push(@result, $current) unless $flag;
    }
    return wantarray ? @result : \@result;
}

1;

Сохрани этот код в файл NewSource.pm, в тот же каталог.
Теперь скопируй такой код и сохрани его в тот же каталог, в файл с любым именем, с раширением .pl.
Код

#!/usr/bin/perl -w
use strict;
use NewSource;

my $source = new NewSource( source => "source.txt" );

# Получаем и выводим список без кодов, которые нам не нужны.
foreach my $current ($source->getListWithoutCodes(1,5)) {
        print $current->{code} . "\n";
        print $current->{name} . "\n";
        print $current->{flag} . "\n";
        print "=" x 79 . "\n";
}


print "\n\n\n\n";

# Устанавливаем бегунок в начальную позицию
$source->toFirst();

# Проходим по всем элементам с помощью бегунка
while (my $current = $source->getCurrent()) {
       print $current->{code} . "\n";
       print $current->{name} . "\n";
       print $current->{flag} . "\n";
       print "-" x 79 . "\n";
       # Переходим к следующей позиции
       $source->toNext();
}

Заметь, что наш новый класс может делать всё тоже, что и старый, мы вправе вызывать всё теже методы, но он так же может возвращаеть нам список без элементов, у которых не нужные нам коды.
При всём этом, мы не переписывали и не копировали в новый класс, код старого класса и ничего не изменяли в классе Source.pm. Мы лишь добавили в новый класс то, чего нам не хватало в старом и при этом можем обращаться к методам класса Source.pm, как к своим собственным.

Ну вот собственно и всё, думаю принцип понятен. ;)))))))

Это сообщение отредактировал(а) korob2001 - 17.8.2006, 07:10


--------------------
"Время проходит", - привыкли говорить вы по неверному пониманию. 
"Время стоит - проходите вы".
PM MAIL WWW ICQ MSN   Вверх
oldlamer
Дата 17.8.2006, 08:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 5
Регистрация: 14.8.2006

Репутация: нет
Всего: нет



то что надо!!!
спасибо, в наследство достался ООПерл-код размером 8М
вот разбираюсь...

ещё раз спасибо...

/wbr?
oldLamer
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Perl: CGI программирование"
korob2001
sharq
  • В этом разделе обсуждаются вопросы относящиеся только к CGI программированию
  • Если ваш вопрос не относится к системному или CGI программированию, задавайте его в общем разделе
  • Если ваш вопрос относится к системному программированию, задавайте его здесь
  • Интерпретатор Perl можно скачать здесь ActiveState, O'REILLY, The source for Perl
  • Справочное руководство "Установка perl-модулей", качать здесь


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, korob2001, sharq.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Perl: разработка для Web | Следующая тема »


 




[ Время генерации скрипта: 0.0693 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.