Включение хэшей в хэши в Perl

Доброго времени суток,

В настоящее время я работаю над созданием больших хешей из множества меньших хешей. Предположим, что эти меньшие хеши определены в файле каждый, а затем могут быть включены в больший хеш.

Например, давайте посмотрим на несколько небольших хэшей

Файл personcontact.pl:

   return {
            \'firstname\' => {
                \'__type\' => \'String\'
            },
        \'lastname\' =>  {
            \'__type\' => \'String\'
            },
        %{include("/tmp/address.pl")}
    }

Файл address.pl:

return {
        \'address\' => {
        \'street\' => {
            \'__type\' => \'String\'
            },
        \'unit\' => {
            \'__type\' => \'String\',
            \'__validation_function\' => {
                \'is_a_number\' => \'\'
            },
            \'__schema_constraints\' => {
                \'is_not_null\' => \'\'
            }
        },
        \'suburb\' => {
            \'__type\' => \'String\'
        },
        \'__type\' => \'ARRAY\'
        }
    } 

А таких у меня немало ...

Я пытаюсь воссоздать хэш с помощью подпрограммы include, которая выглядит так:

 sub include {
my ($filename) = @_;
my $file; 
open(my $fh, "<", $filename) or die ("FILEOPEN: $!");
while(my $line = <$fh>) { $file .= $line; }
my $result = eval $file;
die("EVAL: $@") if $@;
close($fh) or die("FILECLOSE: $!");
return $result;
 }

Я знаю, что, должно быть, делаю что-то не так, но не знаю, что именно. Я продолжаю получать ошибки типа Useless use of a variable in void context at (eval 11) line 4, <SCHEMAFILE> line 6 или Odd number of elements in anonymous hash at (eval 11) line 5, <SCHEMAFILE> line 6. Я не уверен, как найти (eval 11) строку 4-3, строку 6. Будем очень признательны за любые предложения по использованию отладчиков Perl или любые указания на то, где я могу ошибиться.

Спасибо!


person Gaurav Dadhania    schedule 24.01.2011    source источник
comment
Не нужно читать построчно. Используйте режим slurp, поместив local $/; перед чтением файла и изменив while(my $line...) на my $line = <$fh>;.   -  person Mikel    schedule 24.01.2011
comment
Спасибо за совет! Как вы уже догадались, я новичок в Perl :)   -  person Gaurav Dadhania    schedule 24.01.2011
comment
Что-то вроде YAML также может быть более подходящим. search.cpan.org/dist/YAML/lib/YAML.pm   -  person Mikel    schedule 24.01.2011
comment
Для чего $this в строке 2 вашей include подпрограммы?   -  person Mikel    schedule 24.01.2011
comment
Вы в основном переписываете do Perl. Взгляните на perldoc -f do. Еще лучше было бы использовать правильный модуль Perl, но вы можете перейти через этот мост, когда дойдете до него. А пока вы хотите посмотреть, как это делается: perldoc perlmod.   -  person Sdaz MacSkibbons    schedule 24.01.2011
comment
Думаю, YAML - излишек для того, над чем я работаю. Я просто пытаюсь сделать и без того ОГРОМНЫЙ хеш в приложении немного более управляемым. :)   -  person Gaurav Dadhania    schedule 24.01.2011
comment
@Sdaz Ага, do на данный момент должен быть в состоянии выполнить свою работу. Спасибо! Однако я по-прежнему получаю ту же ошибку Useless use of variable in void context at (eval 11) line 3 , <SCHEMAFILE> line 5. Как найти эту строку? Perldiag в этом случае тоже не очень полезен.   -  person Gaurav Dadhania    schedule 24.01.2011
comment
@Mikel typo - Изначально предполагалось, что это ссылка на себя, но это было только для тестирования. Прости.   -  person Gaurav Dadhania    schedule 24.01.2011
comment
Мне нужно было бы увидеть обновленный код, чтобы дать окончательный ответ, но, как правило, где-то вы, вероятно, делаете что-то вроде $foo = ('junk','list','blah'); или в соответствии с этими строками (т. Е. Используя скаляр в контексте списка). Отследить через eval может быть сложно, но проверьте, где вы вызываете do и на что его устанавливаете. Или разместите обновленный код.   -  person Sdaz MacSkibbons    schedule 24.01.2011
comment
Кроме того, похоже, вы также переписываете ORM (объектно-реляционный преобразователь) с нуля, чтобы абстрагироваться от операторов SQL. Для этого есть другие решения, например DBIx::Class, Class::DBI и т. Д.   -  person Sdaz MacSkibbons    schedule 24.01.2011
comment
Вы уверены, что эти обратные косые черты полезны? Я не ожидал, что они будут в файлах данных.   -  person Jonathan Leffler    schedule 24.01.2011
comment
@Jonathan Это работает без обратной косой черты, я просто хотел быть осторожным, они каким-то образом не вызывали проблем :)   -  person Gaurav Dadhania    schedule 24.01.2011
comment
@Sdaz Нет, то, что я пытаюсь сделать, намного проще, чем писать мою ORM, хотя я уверен, что есть модуль, который может сделать это элегантно (но я пытаюсь научиться). Теперь, когда я использую do, я не получаю никаких ошибок, но получаю непоследовательные ответы. [Обновление вопроса]   -  person Gaurav Dadhania    schedule 24.01.2011


Ответы (1)


Добро пожаловать в Perl. Надеюсь, вы хорошо проведете время, изучая и используя его.

К делу, с чего начать? Мне есть что сказать здесь.

Во-первых, загружать данные путем оценки файлов излишне рискованно. Если вы просто хотите сериализовать данные, попробуйте JSON :: XS или YAML или даже Возможность хранения. Если вам нужен файл конфигурации, на CPAN есть много-много модулей, которые помогают с этой задачей. Ознакомьтесь с Config :: Any.

Если вы хотите создать структуры данных для загрузки через eval (что на самом деле не очень хорошая идея), Data: : Dumper генерирует Perl-код, необходимый для создания любых структур данных, которые вы ему скармливаете. Основная причина, по которой я упоминаю об этом, заключается в том, что он гораздо более полезен в качестве средства отладки, чем сериализатор.

Теперь, когда об этом позаботились, если вы хотите загрузить файл и оценить его (опять же, не лучшая идея почти во всех случаях), вам следует посмотреть на do или требуется.

my $stuff = do 'address.pl';

Но не делай этого. String eval - это инструмент, который обычно лучше не использовать. В 99% случаев, если вы планируете использовать строку eval, остановитесь и подумайте о другом способе решения проблемы. Do - это неявный eval, поэтому он тоже считается.

Perl дает вам множество инструментов для выполнения рискованной и мощной магии. Большая часть того, чтобы стать квалифицированным программистом на Perl, заключается в понимании того, что является рискованным, почему и когда имеет смысл их использовать. Не ждите, что Perl будет баловать вас забором и воротами, чтобы вы были в безопасности. Серьезно подумайте о том, чтобы взять копию Эффективное программирование на Perl или Рекомендации по Perl. Когда вы новичок, многое пролетит над вашей головой при первом чтении, но любая книга может стать отличным справочником по мере вашего роста и обучения.

Следующая тема, чего вы, черт возьми, пытаетесь достичь с помощью всех этих экранированных цитат? У меня голова болит, когда я смотрю на это! В Perl есть несколько очень, очень хороших операторов цитирования, которые вы можно использовать, чтобы никогда не возиться с экранированием кавычек в ваших буквальных строках.

=> или жирная запятая автоматически помещает в кавычки левую часть (LHS), как если бы это были только буквы и цифры. Но использование кавычек и отступлений делает вещи действительно изворотливыми.

Когда вы говорите \'address\' => {}, Perl видит это как \, оператор "получения ссылки", применяемый к строковому литералу. В данном случае это незавершенный строковый литерал, потому что вы никогда не предлагаете неэкранированный ' после первого.

Если ваша цель - использовать 'address', кавычки и прочее в качестве хеш-ключа, вы можете сделать это:

my %foo = ( "'address'" => 'blah' );

Если вам не нужны кавычки, что кажется гораздо более обычным вариантом использования, просто выполните:

my %foo = ( address => 'blah' );

Переходим к сообщениям об ошибках, которые вы получали! Если вы узнаете, что все они означают, в Perl появятся довольно приятные сообщения об ошибках. До тех пор может быть немного сложно понять их значение. К счастью, Perl поставляется со скриптом под названием splain: удобный инструмент, который более подробно объясняет сообщения об ошибках. Вы также можете использовать модуль диагностика, чтобы автоматически получать такие же расширенные сообщения об ошибках.

Если бы я писал это, я бы сделал что-нибудь в этом роде:

gen_schema_files.pl - файл для записи файлов схемы JSON. Вы можете вручную редактировать свои схемы, если хотите. Вы также можете настроить вывод, чтобы он был красивее, если хотите улучшить читаемость.

#!/usr/bin/perl

use JSON::XS;
use File::Spec;

use constant BASEDIR => '.';

# Key is the file name, value is the data to put into the file.
my %schemata = (
    'address.json' => {
        address => {
            street => { __type => 'String' },
            unit => {
                __type => 'String',
                __validation_function => { is_a_number => '' },
                __schema_constraints  => { is_not_null => ''  }
            },
            suburb => { __type => 'String' },
            __type => 'ARRAY'
        },
    },

    'person_contact.json' => {
         firstname => { __type => 'String' },
         lastname =>  { __type => 'String' },

         # Use a special key to indicate that additional files should be 
         # loaded into this hash.
         INCLUDE  => [qw( address.json )], 
     },

     # And so forth
);

for my $schema ( keys %schemata ) {
    my $path = File::Spec->catfile( BASEDIR, $schema );

    open my $fh, '>', $path
        or die "Error opening '$path' for writing - $!\n";

    print $fh encode_json $schemata{$schema};
}

load_schemas.pl - это код, который загружает схемы и делает разные вещи. Моя только заряжается. Понятия не имею, что вы делаете с данными ...

#!/usr/bin/perl
use strict;
use warnings;

use Data::Dumper;

use JSON::XS;
use File::Spec;

use constant BASEDIR => '.';


my $schema = load_schema( 'person_contact.json' );

print Dumper $schema;


sub load_schema {
    my $file = shift;

    my $path = File::Spec->catfile( BASEDIR, $file );

    open my $fh, '<', $path
        or die "Error opening file '$path' - $!\n";

    my $json = join '', <$fh>; # reads a list of lines and cats them into one string.
                               # One way to slurp out of many.

    my $schema = decode_json( $json );

    # Handle the inclusion stuff:

    if( exists $schema->{INCLUDE} ) {
        # Copy the files to load into an array.
        my @loadme = @{$schema->{INCLUDE}};
        # delete the magic special include key.
        delete $schema->{INCLUDE};

        # Load each file and copy it into the schema hash.
        for my $load ( @loadme ) {
            my $loaded = load_schema( $load );

            # This is a bit of weird syntax.
            # We are using a hash slice assignment to copy the loaded data into the existing hash.
            # keys and values are guaranteed to come out in the same (random) order as each other.
            # the @{$foo}{blahbhal} is how you dereference a hash reference as a slice.
            @{$schema}{keys %$loaded} = values %$loaded;
        }
    }

    return $schema;
}

Я замалчивал некоторые вещи, но я попытался оставлять комментарии с достаточным количеством терминов (словарный запас или даже жаргон, если хотите), чтобы вы могли выполнять эффективный поиск.

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

Perldoc - потрясающий ресурс, но его так много, что нужно время, чтобы научиться находить нужные вещи. Взгляните на Поваренную книгу Perl Data Structures и Руководство по массивам массивов. Как новичок, я обнаружил, что в разделе Perl-функции по категориям perlfunc быть невероятно полезным.

Думаю, я остановлюсь сейчас, когда я написал более чем достаточно текста, чтобы ослепить обычного человека. Я надеюсь, что эта диссертация окажется для вас полезной. Еще раз добро пожаловать и добрый вечер (пожалуйста, выберите свое местное время, когда найдете этот ответ).

person daotoad    schedule 24.01.2011