Вложенные массивы perl, хэши и ссылки

#perl

Вопрос:

Я пробовал различные комбинации @%$s, но не могу заставить это работать. Полное выражение работает, но как мне разделить его на переменные?

 print "BIGexp ", $tables{"grp"}[1]{_name}, "n"; # OK, prints part
my @cols = $tables{"grp"};
print "COLS ", @cols, "n"; # prints array
my %col = %cols[1]; 
print "COLvar ", %col, " ", "n"; #prints 1 ?
print "COLexp ", $cols[1]{_name}, "n";  ######## Fails, uninitialized.
 

Я хочу представить схемы баз данных. Итак, карта таблиц, каждая из которых содержит массив столбцов, каждый из которых является картой. Вот полный фрагмент.

 my %tables=();

my $columns; # array

sub addTable {
  my @cols = ();
  $tables{$_[0]} = @cols;
  $columns = @cols;
}

sub addColumn {
    my %col  = (_name => $_[0],  _typ => $_[1]);
    push(@$columns , %col);
}

addTable("grp");
addColumn("id", "serial");
addColumn("part", "integer");

addTable("tab2");
addColumn("foo2", "varchar");
addColumn("bar2", "integer");

print "BIGexp ", $tables{"grp"}[1]{_name}, "n"; # OK, prints part
my @cols = $tables{"grp"};
print "COLS ", @cols, "n"; # prints array
my %col = %cols[1]; 
print "COLvar ", %col, " ", "n"; #prints 1 ?
print "COLexp ", $cols[1]{_name}, "n";  ######## Fails, uninitialized.
 

Я новичок в Perl и еще не стал «настроенным на Perl» :(. Любая помощь очень ценится.

Комментарии:

1. Быстрое (но необходимое) чтение, чтобы начать работу со ссылками: perldoc.perl.org/perlreftut

2. «мой %col = %cols[1];» : Попробуйте my %col = %{$cols->[1]}

3. «Я пробовал различные комбинации @%$s». Мне больше всего нравится в этом вопросе то, что похоже, что вы подвергаете цензуре ругательство, но вы просто точно описываете синтаксис Perl.

4. @Данные да, эта ссылка очень хороша и дала мне ответ. Спасибо.

Ответ №1:

Вы можете использовать модуль use Data::Dumper; для печати ваших структур данных.

 print "BIGexp ", $tables{"grp"}[1]{_name}, "n"; # OK, prints part
my @cols = $tables{"grp"};
print "COLS ", @cols, "n"; # prints array
my %col = %cols[1]; 
print "COLvar ", Dumper(%col), " ", "n"; #prints 1 ?
print "COLexp ", Dumper(@cols), "n";  ######## Fails, uninitialized.

print "COLexp ", Dumper($cols[0][1]{_name}), "n";
 

И результат такой:

    BIGexp part
COLS ARRAY(0x800e5f480)
COLvar $VAR1 = {
          '1' => undef
        };
 
COLexp $VAR1 = [
          [
            {
              '_typ' => 'serial',
              '_name' => 'id'
            },
            {
              '_name' => 'part',
              '_typ' => 'integer'
            }
          ]
        ];

COLexp $VAR1 = 'part';
 

Как вы можете видеть, на самом деле у вас есть вложенный массив. Поэтому вам нужно сначала получить доступ к вложенному массиву, а затем к хэшу.

Комментарии:

1. А? Как он стал вложенным? И какое заклинание требуется, чтобы его отменить? [0] не помогает. И что именно такое $%@.. Это своего рода просто синтаксический шум, но также и своего рода операторы, которые меняют семантику. Я называю их «перлами». Спасибо, но

2. Он становится вложенным здесь : my @cols = $tables{"grp"}; в основном вы создаете новый массив и внутри него добавляете свой массив из хэша %таблиц. Вы можете увидеть это с помощью: print "Inside tables", Dumper($tables{"grp"}),"n"; теперь есть только один массив с хэшами.

3. Вы можете отменить его, выполнив: my @arr = map {$_->[0]} @cols; print Dumper(@arr); это выведет первый элемент массива, который является тем массивом, который вам нужен.

Ответ №2:

Следующий пример демонстрационного кода предоставляет один из многих возможных способов формирования того, что ОП назвал базой данных shema.

Функция OP in addColumn($name, $type) не указывает, к какой таблице принадлежит столбец.

Было бы неплохо посмотреть, какую окончательную структуру данных OP ожидает построить, чтобы уточнить, каким будет конечный результат.

Пример кода предоставлен в надежде, что он может помочь оператору в поиске решения его/ее проблемы.

 use strict;
use warnings;
use feature 'say';

use Data::Dumper;

my %tables;

tbAddColumn('grp','id','serial');
tbAddColumn('grp','part','integer');

tbAddColumn('tab2','foo2','varchar');
tbAddColumn('tab2','bar2','integer');

say Dumper(%tables);

my $tb_name = 'grp';

say "Table: $tb_name";
say '-' x 45;

for my $column ( @{$tables{$tb_name}{columns}} ) {
    say "name => $column->{name}, type => $column->{type}";
}

exit 0;

sub tbAddColumn {
    my $tbname = shift;
    my $name   = shift;
    my $type   = shift;

    push @{$tables{$tbname}{columns}}, { name => $name, type => $type};
}
 

Выход

 $VAR1 = {
          'tab2' => {
                      'columns' => [
                                     {
                                       'name' => 'foo2',
                                       'type' => 'varchar'
                                     },
                                     {
                                       'name' => 'bar2',
                                       'type' => 'integer'
                                     }
                                   ]
                    },
          'grp' => {
                     'columns' => [
                                    {
                                      'name' => 'id',
                                      'type' => 'serial'
                                    },
                                    {
                                      'type' => 'integer',
                                      'name' => 'part'
                                    }
                                  ]
                   }
        };

Table: grp
---------------------------------------------
name => id, type => serial
name => part, type => integer
 

ПРИМЕЧАНИЕ: OP мог бы немного почитать об объектно-ориентированном программировании на Perl, которое позволило бы инкапсулировать табличные данные/структуру в объект. Код результата будет выглядеть намного чище и проще в обслуживании.

Образец псевдокода

 ......
my $tb1 = new dbTable('grp');
my $tb2 = new dbTable('tab2');

$tb1->addColumn('id','serial');
$tb1->addColumn('part','integer');


$tb2->addColumn('foo2','varchar');
$tb2->addColumn('bar2','integer');
.....
 

Комментарии:

1. OP использовал глобальную переменную для хранения текущей таблицы ($columns), чтобы сделать вызовы немного более краткими, и, похоже, она создает правильную структуру в соответствии с Dumper. Мог бы использовать другой стиль. Но вы не отвечаете на мой вопрос, а именно, как мне индексировать созданную структуру. (Я также не понимаю вашего причудливого толчка, но вопрос не в этом.)

2. Вы обращаетесь к структуре следующим образом $tables{$tbname}{columns}[$index]{$key} (или вы можете использовать $tables{'grp'}{'columns'}[0]{'name'} , например, если ключ состоит из одного слова, то кавычки могут быть опущены). $tables{$tbname}{columns} является arrayref , вы не можете вставить элемент в arrayref , он требует разыменования @{$arrayref} .

3. @Tuntable Я отредактировал ваш вопрос с добавлением, демонстрирующим доступ к сохраненным данным, хотя и создал копию массива/хэша в соответствии с вашим примером кода (в данном конкретном случае это не нужно, так как копирование требует циклов памяти и процессора). Если вам не нужны дубликаты данных, то доступ может быть обеспечен с помощью ссылки на массив/хэш-это намного быстрее, не потребляет память и экономит циклы процессора .

4. @PolarBear Я откатил вашу правку по этому вопросу. Код, который вы добавили, пришел без объяснений, и текст содержался в ответе, а не в вопросе. Помните, что вы можете опубликовать несколько ответов, если захотите.

5. @Dada — это, если меня устраивает, меня больше не волнует этот вопрос.

Ответ №3:

Синтаксис %cols[1] представляет собой срез массива значений индекса. Если @cols есть только один элемент, значение индекса 1 не определено и %cols[1] возвращает список (1, undef) , которому присвоена %col строка 4 в вашем первом фрагменте:

 my %col = %cols[1];
 

Итак, сейчас %col = ("1" => undef) и когда вы пытаетесь распечатать его в строке 5:

 print "COLvar ", %col, " ", "n";
 

Он пытается напечатать ключ «1», соединенный со значением undef , которое выдает предупреждение, а затем вывод:

 Use of uninitialized value in print
COLvar 1
 

Комментарии:

1. А? $cols[1] не работает. Так как же можно не делать срез и не делать простой индекс?

2. @Tuntable В данном случае $#cols == 0 так $cols[1] не существует. Я не уверен, что вы подразумеваете под «не работает» . Пожалуйста, уточните

Ответ №4:

Спасибо за сообщения, но ни одно из них не решило реальную проблему. Решение заключается в следующем

 print "BIGexp ", $tables{"grp"}[1]{_name}, "n"; # OK, part
my $cols = $tables{"grp"};  # ref array
my $col = ${$cols}[1];  # ref hash
my %colh = %{$col}; # hash
print "COLvar ", $colh{_name}, " ", "n"; # bingo
print "COLvar2 ", ${$col}{_name}, " ", "n"; # bingo
print "COL-> ", $col->{_name}, " ", "n"; # bingo
 

Хитрость заключается в том, чтобы позаботиться о разнице между массивом ссылок и массивом и т. Д. {$x} — оператор разыменования.

Я все еще не совсем понимаю, что на самом деле делает «%» и т. Д. Я подозреваю, что лучше всего использовать только ссылки и полностью избегать % и @.

Эта ссылка была очень полезной https://perldoc.perl.org/perlreftut

Комментарии:

1. Прежде чем перейти к этому представлению, пожалуйста, ознакомьтесь также с другим разделом документов Perl, а именно с разделом о переменных Perl: perldoc.perl.org/perlvar .