три вопроса о функции Perl - PullRequest
2 голосов
/ 09 января 2012

Я пытаюсь использовать существующую программу Perl, которая включает следующую функцию GetItems. Способ вызова этой функции указан в следующем.

У меня есть несколько вопросов к этой программе:

  1. что намеревается сделать foreach my $ref (@_)? Я думаю @_ должно быть связано с переданными параметрами, но не совсем уверен.

  2. В my @items = sort { $a <=> $b } keys %items; «элементы» на левой стороне должны отличаться от «элементов» на правой стороне? Почему они используют одно и то же имя?

  3. Что намеревается сделать $items{$items[$i]} = $i + 1;? Похоже, он просто устанавливает значение для хеша $items последовательно.

$items = GetItems($classes, $pVectors, $nVectors, $uVectors);

######################################
sub GetItems
######################################

{
    my $classes = shift;
    my %items = ();
    foreach my $ref (@_)
    {
        foreach my $id (keys %$ref) 
        { 
            foreach my $cui (keys %{$ref->{$id}}) { $items{$cui} = 1 }
        }
    }

    my @items = sort { $a <=> $b } keys %items;

    open(VAL, "> $classes.items");
    for my $i (0 .. $#items)
    {
        print VAL "$items[$i]\n";
        $items{$items[$i]} = $i + 1;
    }
    close VAL;
    return \%items;
}

Ответы [ 5 ]

3 голосов
/ 09 января 2012
  1. Когда вы вводите функцию, @_ начинается как массив (псевдонимов) всех параметров, передаваемых в функцию;но my $classes = shift удаляет первый элемент @_ и сохраняет его в переменной $classes, поэтому foreach my $ref (@_) выполняет итерации по всем оставшимся параметрам, сохраняя (псевдонимы) их по одномувремя в $ref.

  2. Скаляры, хэши и массивы различаются по синтаксису, поэтому им разрешено иметь одно и то же имя.Вы можете иметь одновременно $foo, @foo и %foo, и они не должны иметь никакого отношения друг к другу.(Это, вместе с тем, что $foo[0] относится к @foo и $foo{'a'} относится к %foo, вызывает много путаницы для новичков в языке; вы не одиноки.)

  3. Точно.Он устанавливает каждый элемент в %items в отдельное целое число в диапазоне от одного до количества элементов, начиная с нумерации (!) По ключу.

3 голосов
/ 09 января 2012
  1. foreach my $ref (@_) перебирает каждую хеш-ссылку, переданную в качестве параметра в GetItems. Если вызов выглядит так:

    $items = GetItems($classes, $pVectors, $nVectors, $uVectors);
    

    затем цикл обрабатывает хэш-ссылки в $pVector, $nVectors и $uVectors.

  2. @items и %items - ПОЛНОСТЬЮ РАЗНЫЕ ПЕРЕМЕННЫЕ !! @items является переменной массива, а %items является хеш-переменной.

  3. $items{$items[$i]} = $i + 1 делает именно так, как вы говорите. Он устанавливает значение хеша %items, ключ которого от $items[$i] до $i+1.

0 голосов
/ 09 января 2012

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


Уменьшите количество циклов for:

Прежде всего, для этого цикла не нужно устанавливать значение $items{$cui} для чего-либо конкретного.Это также не обязательно должен быть цикл.

foreach my $cui (keys %{$ref->{$id}}) { $items{$cui} = 1 }

Это делает практически то же самое.Единственная реальная разница состоит в том, что вместо этого они устанавливаются на undef.

@items{ keys %{$ref->{$id}} } = ();

Если вам действительно нужно было установить значения на 1.Обратите внимание, что (1)x@keys возвращает список 1 с таким же количеством элементов в @keys.

my @keys = keys %{$ref->{$id}};
@items{ @keys } = (1) x @keys;

Если вы собираетесь перебирать очень большое количество элементов, тоЦикл for может быть хорошей идеей, но только если вам нужно установить значение, отличное от undef.Так как мы используем переменную цикла только один раз, чтобы сделать что-то простое;Я бы использовал этот код:

$items{$_} = 1 for keys %{$ref->{$id}};

Обмен keys с values:

На линии до этогомы видим:

foreach my $id (keys %$ref){

В случае, если вы не заметили, $id использовался только один раз, и это было для получения соответствующего значения.

Это означает, что мы можем использовать values и избавьтесь от синтаксиса %{$ref->{$id}}.

for my $hash (values %$ref){
  @items{ keys %$hash } = ();
}

($hash не хорошее имя, но я не знаю, что этопредставляет.)


3 arg open:

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

open(VAL, "> $classes.items");

Кроме того, знаете ли вы, что существует также форма с одним аргументом open.Хотя я не очень рекомендую это, в основном это для обратной совместимости.

our $VAL = "> $classes.items";
open(VAL);

Рекомендованный способ сделать это - с 3 аргументами.

open my $val, '>', "$classes.items";

В некоторых редких случаях можно / нужно использовать версию с двумя аргументами.


Соберите все вместе:

sub GetItems {
  # this will cause open and close to die on error (in this subroutine only)
  use autodie;

  my $classes = shift;
  my %items;

  for my $vector_hash (@_){
    # use values so that we don't have to use $ref->{$id}
    for my $hash (values %$ref){
       # create the keys in %items
       @items{keys %$hash} = ();
    }
  }

  # This assumes that the keys of %items are numbers
  my @items = sort { $a <=> $b } keys %items;
  # using 3 arg open
  open my $output, '>', "$classes.items";

  my $index; # = 0;
  for $item (@items){
    print {$output} $item, "\n";
    $items{$item} = ++$index; # 1...
  }

  close $output;
  return \%items;
}

Другая опция для этого последнего for цикла.

  for my $index ( 1..@items ){
    my $item = $items[$index-1];
    print {$output} $item, "\n";
    $items{$item} = $index;
  }

Если ваша версия Perl 5.12 или новее, вы можете написать эту последнюю for цикл такой:

  while( my($index,$item) = each @items ){
    print {$output} $item, "\n";
    $items{$item} = $index + 1;
  }
0 голосов
/ 09 января 2012

У меня есть несколько вопросов к этой программе:

  • Что намеревается сделать foreach my $ref (@_)? Я думаю, что @_ должен быть связан с переданными параметрами, но не совсем уверен.

Да, вы правы. Когда вы передаете параметры в подпрограмму, они автоматически помещаются в массив @_. (Называется список в Perl). foreach my $ref (@_) начинает цикл. Этот цикл будет повторяться для каждого элемента в массиве @_, и каждый раз значению $ref будет назначаться следующий элемент в массиве. См. Раздел Perlsoc Perlsyn (синтаксис Perl) о for циклах и foreach циклах . Также посмотрите раздел Perloc Perlvar (Perl Variables) в Общие переменные для получения информации о специальных переменных, таких как @_.

Теперь строка my $classes = shift; удаляет первый элемент в списке @_ и помещает его в переменную $classes. Таким образом, цикл foreach будет повторен три раза. Каждый раз для $ref сначала устанавливается значение $pVectors, $nVectors и, наконец, $uVectors.

.

Кстати, это не совсем скалярные значения. В Perl вы можете получить то, что называется reference. Это место в памяти структуры данных, на которую вы ссылаетесь . Например, у меня пять учеников, и у каждого ученика есть серия тестов, которые они прошли. Я хочу сохранить все значения каждого теста в хэш-ключе, идентифицируемом идентификатором студента.

Обычно каждая запись в хэше может содержать только один элемент. Однако что, если этот пункт относится к списку, который содержит оценки ученика?

Вот список учеников № 100:

@grades = (100, 93, 89, 95, 74);

А вот как я устанавливаю запись Student 100 в свой хэш:

$student{100} = \@grades;

Теперь я могу говорить о первом классе года для ученика № 100 как $student{100}[0]. См. Очень короткий урок Perldoc Mark о ссылках .

  • В my @items = sort { $a <=> $b } keys %items; «элементы» на левой стороне должны отличаться от «элементов» на правой стороне? Почему они используют одно и то же имя?

В Perl у вас есть три основных типа переменных: Списки (что некоторые люди называют Массивы ), Хэши (что некоторые люди называют Keyed Arrays ) и Scalars . В Perl совершенно законно, чтобы разные типы переменных имели одно и то же имя. Таким образом, вы можете иметь $var, %var и @var в вашей программе, и они будут рассматриваться как полностью отдельные переменные 1 .

Обычно это плохая вещь , и это крайне обескураживает. Ситуация ухудшается, когда вы думаете об отдельных значениях: $var относится к скаляру, а $var[3] относится к списку, а $var{3} относится к хешу. Да, это может быть очень, очень запутанным.

В этом конкретном случае у него есть хеш (массив ключей) с именем %item, и он преобразует ключи в этом хеш-коде в список, отсортированный по ключам. Этот синтаксис может быть упрощен с:

my @items = sort { $a <=> $b } keys %items;

просто:

my @items = sort keys %items;

См. Perldocs о функции sort и о клавишах .

  • Что означает $ items {$ items [$ i]} = $ i + 1; цель сделать? Похоже, он просто устанавливает значение для элементов хеша $ последовательно.

Давайте посмотрим на весь цикл:

foreach my $i (0 .. $#items)
{
    print VAL "$items[$i]\n";
    $items{$items[$i]} = $i + 1;
}

Подпрограмма будет проходить этот цикл один раз для каждого элемента в списке @items. Это отсортированный список ключей для старого хэша %items. $#items означает самый большой индекс в списке предметов. Например, если @items = ("foo", "bar", and "foobar"), то $#item будет 2, поскольку последний элемент в этом списке - $item[2], что равно foobar.

Таким образом, он достигает индекса каждой записи в @items. ( ПОМНИТЕ : отличается от %item!).

Следующая строка немного хитрая:

$items{$items[$i]} = $i + 1;

Помните, что $item{} относится к старому %items хешу!Он создает новый %items хэш.Для этого используется каждый элемент в списке @items.И в качестве значения указывается индекс этого элемента плюс 1. Предположим, что:

@items = ("foo", "bar", "foobar")

В конце он делает это:

$item{foo} = 1;
$item{bar} = 2;
$item{foobar} = 3;

1 Ну, это не на 100% верно.Perl хранит каждую переменную в виде хэш-структуры.В памяти $var, @var и %var будут храниться в той же хэш-записи в памяти, но в позициях, связанных с каждым типом переменной.В 99,9999% случаев это не имеет значения.Насколько вам известно, это три совершенно разные переменные.

Тем не менее, есть несколько редких случаев, когда программист воспользуется этим преимуществом, когда они напрямую работают с памятью в Perl.

0 голосов
/ 09 января 2012

Вот (почти) построчное описание того, что происходит в подпрограмме


Определить подпункт с именем GetItems.

sub GetItems {

Сохранить первое значение в массиве по умолчанию @_ и удалить его из массива.

  my $classes = shift;

Создать новый хеш с именем %items.

  my %items;

Цикл по оставшимся значениям, заданным для подпрограммы, устанавливая $ref в значение на каждой итерации.

  for my $ref (@_){

В этом коде предполагается, что в предыдущей строке для $ref установлено значение ref. Он перебирает несортированные ключи хэша, на которые ссылается $ref, сохраняя ключ в $id.

    for my $id (keys %$ref){

Используя ключ ($id), указанный в предыдущей строке, переберите ключи хеш-ссылки в этой позиции в $ref. При этом также устанавливая значение $cui.

      for my $cui (keys %{$ref->{$id}}) {

Установите значение %item в положении $cui, на 1.

        $items{$cui} = 1;

Конец циклов на предыдущих строках.

      }
    }
  }

Сохраняет sort ed список клавиш %items в @items в соответствии с числовым значением.

  my @items = sort { $a <=> $b } keys %items;

Откройте файл с именем $classes с добавлением .items. При этом используется двухарговая форма старого стиля open. Он также игнорирует возвращаемое значение open, поэтому он переходит к следующей строке даже при ошибке. Он хранит дескриптор файла в глобальном *VAL{IO}.

  open(VAL, "> $classes.items");

Цикл по списку индексов @items.

  for my $i (0 .. $#items){

Вывести значение этого индекса в отдельной строке до *VAL{IO}.

    print VAL "$items[$i]\n";

Использование того же значения в качестве индекса в %items (от которого он является ключом) до индекса плюс один.

    $items{$items[$i]} = $i + 1;

Конец цикла.

  }

Закрыть дескриптор файла *VAL{IO}.

  close VAL;

Вернуть ссылку на хеш %items.

  return \%items;

Конец подпрограммы.

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...