Включая хэши в хэши в Perl - PullRequest
6 голосов
/ 24 января 2011

G'Day,

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

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

Файл 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 или указателей на то, где я могу ошибаться.

Спасибо!

1 Ответ

11 голосов
/ 24 января 2011

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

В бизнесе, с чего начать?Здесь мне есть, что сказать.

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

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

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

my $stuff = do 'address.pl';

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

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

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

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

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

Если ваша цель - использовать '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 Cookbook и учебник Arrays of Arrays .Как новичок, я нашел невероятно полезным раздел Функции Perl по категориям perlfunc .

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

...