Как мне добавить элементы из одного массива Perl, которых еще нет в другом массиве?

Дано:

my @mylist1;
push(@mylist1,"A");
push(@mylist1,"B");
push(@mylist1,"C");

my @mylist2;
push(@mylist2,"A");
push(@mylist2,"D");
push(@mylist2,"E");

Какой самый быстрый способ в Perl вставить в mylist2 все элементы, которые есть в mylist1, но еще не в mylist2 (ABCDE).

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
10
0
9 621
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий
my %k;
map { $k{$_} = 1 } @mylist1;
map { $k{$_} = 1 } @mylist2;
@mylist2 = keys %k;

Альтернативно:

my %k;
map { $k{$_} = 1 } @mylist2;
push(@mylist2, grep { !exists $k{$_} } @mylist1);

На самом деле - они могут быть неправильными, потому что они не учитывают, могут ли дубликаты существовать в любом из исходных списков.

Вы не сказали в своем вопросе, должны ли списки представлять наборы (которые не могут содержать дубликатов) или просто списки. То, что вы действительно хотите @mylist2 = @mylist1 U @mylist2, предполагает, что вы рассматриваете их как наборы.

Обновлено: изменено приращение для назначения - сохраняет чтение хеш-значения

Это нормально, если вам не нужно сохранять исходный порядок.

Brad Gilbert 29.11.2008 19:49

Второй вариант - самый быстрый по моим измерениям - и быстрее, чем метод uniq в List :: MoreUtils.

Jonathan Leffler 30.11.2008 10:04

[Оригинальный ответ от 27.11.2008 до «С вопроса»; анализ с этого момента является новым по состоянию на 29 ноября 2008 года.]

Самый быстрый - не уверен. Это работает, хотя и некрасиво:

#!/bin/perl -w
use strict;

my @mylist1;
push(@mylist1,"A");
push(@mylist1,"B");
push(@mylist1,"C");

my @mylist2;
push(@mylist2,"A");
push(@mylist2,"D");
push(@mylist2,"E");

sub value_in
{
    my($value, @array) = @_;
    foreach my $element (@array)
    {
        return 1 if $value eq $element;
    }
    return 0;
}

@mylist2 = (@mylist2, grep { ! value_in($_, @mylist2) } @mylist1);

print sort @mylist2, "\n";

Это позволяет избежать преобразования массивов в хэши, но для больших массивов вспомогательная функция value_in может работать медленно.

Поскольку вопрос был в том, «какой метод самый быстрый», я провел несколько тестов. К моему не слишком большому удивлению, мой метод оказался самым медленным. К моему удивлению, самый быстрый метод был не из List :: MoreUtils. Вот тестовый код и результаты - с использованием модифицированной версии моего первоначального предложения.

#!/bin/perl -w
use strict;
use List::MoreUtils  qw(uniq);
use Benchmark::Timer;

my @mylist1;
push(@mylist1,"A");
push(@mylist1,"B");
push(@mylist1,"C");

my @mylist2;
push(@mylist2,"A");
push(@mylist2,"D");
push(@mylist2,"E");

sub value_in
{
    my($value) = shift @_;
    return grep { $value eq $_ } @_;
}

my @mylist3;
my @mylist4;
my @mylist5;
my @mylist6;

my $t = Benchmark::Timer->new(skip=>1);
my $iterations = 10000;

for my $i (1..$iterations)
{
    $t->start('JLv2');
    @mylist3 = (@mylist2, grep { ! value_in($_, @mylist2) } @mylist1);
    $t->stop('JLv2');
}
print $t->report('JLv2');

for my $i (1..$iterations)
{
    $t->start('LMU');
    @mylist4 = uniq( @mylist1, @mylist2 );
    $t->stop('LMU');
}
print $t->report('LMU');

for my $i (1..$iterations)
{
    @mylist5 = @mylist2;
    $t->start('HV1');
    my %k;
    map { $k{$_} = 1 } @mylist5;
    push(@mylist5, grep { !exists $k{$_} } @mylist1);
    $t->stop('HV1');
}
print $t->report('HV1');

for my $i (1..$iterations)
{
    $t->start('HV2');
    my %k;
    map { $k{$_} = 1 } @mylist1;
    map { $k{$_} = 1 } @mylist2;
    @mylist6 = keys %k;
    $t->stop('HV2');
}
print $t->report('HV2');


print sort(@mylist3), "\n";
print sort(@mylist4), "\n";
print sort(@mylist5), "\n";
print sort(@mylist6), "\n";

Black JL: perl xxx.pl
9999 trials of JLv2 (1.298s total), 129us/trial
9999 trials of LMU (968.176ms total), 96us/trial
9999 trials of HV1 (516.799ms total), 51us/trial
9999 trials of HV2 (768.073ms total), 76us/trial
ABCDE
ABCDE
ABCDE
ABCDE
Black JL:

Это Perl 5.10.0, скомпилированный для 32-битного SPARC с множественностью на старинном Sun E450 под управлением Solaris 10.

Я считаю, что тестовые установки правильные; все они генерируют свой ответ в новый массив, отдельный от mylist1 и mylist2 (так что mylist1 и mylist2 могут быть повторно использованы для следующего теста). Ответ, обозначенный как HV1 (значения хэша 1), имеет начало отсчета времени после присвоения @ mylist5, что я считаю правильным. Однако, когда я рассчитал время со старта перед назначением, он все равно был самым быстрым:

Black JL: perl xxx.pl
9999 trials of JLv2 (1.293s total), 129us/trial
9999 trials of LMU (938.504ms total), 93us/trial
9999 trials of HV1 (505.998ms total), 50us/trial
9999 trials of HV2 (756.722ms total), 75us/trial
ABCDE
ABCDE
ABCDE
ABCDE
9999 trials of HV1A (655.582ms total), 65us/trial
Black JL:

Из-за вашего комментария «(ABCDE)» я предполагаю, что вы на самом деле имели в виду поместить в mylist1 те элементы в mylist2, которых нет в mylist1. Если это предположение неверно, вам нужно сказать что-то о том, в каком порядке вы хотите, чтобы все закончилось.

Сначала сохраните в хэше, какие элементы находятся в mylist1, затем поместите все элементы в mylist2, не найденные в хеше, в mylist1.

my %in_mylist1;
@in_mylist1{@mylist1} = ();
push @mylist1, grep ! exists $in_mylist1{$_}, @mylist2;

Вы можете просто использовать List::MoreUtils модуля uniq:

use List::MoreUtils qw(uniq);

my @mylist1;
push( @mylist1, "A" );
push( @mylist1, "B" );
push( @mylist1, "C" );

my @mylist2;
push( @mylist2, "A" );
push( @mylist2, "D" );
push( @mylist2, "E" );

@mylist2 = uniq( @mylist1, @mylist2 );

printf "%s\n", ( join ',', @mylist2 );    # A,B,C,D,E

Умение определять и использовать модули - довольно важная часть изучения Perl.

oeuftete 27.11.2008 23:55

Конечно, но вы все равно должны знать основы

Alnitak 28.11.2008 01:08

Я бы хотел, чтобы больше людей использовали List :: MoreUtils вместо того, чтобы писать одни и те же маленькие кусочки кода снова и снова.

Rob Van Dam 14.01.2010 08:49
my(%work);
@work{@mylist1, @mylist2} = undef;
@mylist2 = sort keys %work;

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

noswonky 16.03.2010 08:37

Другие вопросы по теме