Как получить уникальные элементы из Perl списка

Задача. В программе на языке программирования Perl есть список. В списке есть строки и числа. Нужно получить уникальные значения из этого списка.

Решение с помощью функции uniq из библиотеки List::Util

Самый простой и удобный способ для того чтобы решить эту задачу — это использовать функцию uniq из библиотеки List::Util. Вот пример кода:

▶ Run
#!/usr/bin/perl

use List::Util qw(uniq);
use Data::Dumper;

my @arr = uniq('one', 'one', 'b', 'one', 'b', 4);

print Dumper \@arr;

Если сохранить этот текст в файл script.pl, а потом выполнить в консоли perl script.pl, то на экране появится:

$VAR1 = [
          'one',
          'b',
          4
        ];

Функция uniq сохраняет порядок элементов и она корректно работает если в списке есть undef.

Этот код будет работать начиная с версии Perl 5.26. В более ранних версиях Perl содержится версия библиотеки List::Util в которой еще нет функции uniq. Так что для того чтобы использовать этот решение нужно либо обновить Perl, либо поставить более свежую версию библиотеки List::Util.

Официальная документация

Вот фрагмент вывода команды perldoc List::Util про функцию uniq:

  uniq
        my @subset = uniq @values

    *Since version 1.45.*

    Filters a list of values to remove subsequent duplicates, as judged by a
    DWIM-ish string equality or "undef" test. Preserves the order of unique
    elements, and retains the first value of any duplicate set.

        my $count = uniq @values

    In scalar context, returns the number of elements that would have been
    returned as a list.

    The "undef" value is treated by this function as distinct from the empty
    string, and no warning will be produced. It is left as-is in the
    returned list. Subsequent "undef" values are still considered identical
    to the first, and will be removed.

Собственное решение

Достаточно просто самостоятельно написать упрощенный вариант функции uniq из библиотеки List::Util:

▶ Run
#!/usr/bin/perl

use Data::Dumper;

sub uniq {
     my (@values) = @_;

     my %h = map {$_ => 1} @values;

     return keys %h;
}

my @arr = uniq('one', 'one', 'b', 'one', 'b', 4);

print Dumper \@arr;

Этот код сразу же работает на любой версии Perl. Вот три строчки из сабы uniq:

  • my (@values) = @_; — разместили аргументы, которые переданы в сабу в переменную @values
  • my %h = map {$_ => 1} @values; — для каждого элемента из @values создали список из двух элементов — изначальный элемент и единица, и все получившиеся списки объединили в один список и присвоили этот список хешу. Хеш — это неупорядоченный набор пар ключ-значение. По одному ключу может быть только одно значение. Благодаря этому свойству хеша убираются все дубли.
  • return keys %h; — вернули список состоящий только из ключей хеша

Но по сравнению с функцией uniq из библиотеки List::Util у этого кода есть недостатки:

  • наша функция uniq возвращает элементы в произвольном порядке. Разные запуски одного и того же кода приведет к разным результатам (значения которые возвращает функция uniq будут одни и те же, но вот порядок в котором они расположены будет отличаться)
  • наша функция некорректно работает если в списке есть undef. Мы используем все значения из списка в качестве ключей хеша. А undef не может быть ключом в хеше. При попытке использовать undef в качестве ключа будет предупреждение и будет использована пустая строка вместо undef.

Другие статьи

Комментарии