2011-01-24 9 views
6

G'Day,Incluyendo Hashes dentro de Hashes en Perl

Actualmente estoy trabajando en la creación de grandes hashes a partir de una gran cantidad de valores hash más pequeños. Digamos que estos hashes más pequeños se definen en un archivo cada uno, y luego pueden ser incluidos por el hash más grande.

Por ejemplo, vamos a ver algunos pequeños hashes

archivos personcontact.pl:

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

Archivo 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\' 
     } 
    } 

y tengo un número considerable de estos ...

La forma en que estoy tratando de volver a crear el tiene h está utilizando la subrutina include, que se ve así:

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: [email protected]") if [email protected]; 
close($fh) or die("FILECLOSE: $!"); 
return $result; 
} 

Sé que debo estar haciendo algo mal, pero no estoy seguro de qué. Sigo recibiendo errores como Useless use of a variable in void context at (eval 11) line 4, <SCHEMAFILE> line 6 o Odd number of elements in anonymous hash at (eval 11) line 5, <SCHEMAFILE> line 6. No estoy seguro de cómo encontrar (eval 11) línea 4-3, línea 6. Cualquier sugerencia sobre el uso de los depuradores Perl o cualquier sugerencia sobre dónde podría estar yendo mal será muy apreciada.

Gracias!

+2

No es necesario leer línea por línea. Use "modo sorber" poniendo 'local $ /;' antes de leer el archivo, y cambie 'while (my $ line ...)' a 'my $ line = <$fh>;'. – Mikel

+0

¡Gracias por el consejo! Como habrás adivinado, soy bastante nuevo en Perl :) –

+3

Algo como YAML también puede ser más apropiado. http://search.cpan.org/dist/YAML/lib/YAML.pm – Mikel

Respuesta

11

Bienvenido a Perl. Espero que lo pases bien aprendiendo y usándolo.

Por negocios, ¿dónde empezar? Tengo mucho que decir aquí.

En primer lugar, es innecesariamente arriesgado cargar datos mediante la evaluación de archivos. Si solo desea serializar los datos intente JSON::XS o YAML, o incluso Storable. Si desea un archivo de configuración, hay muchos, muchos módulos en CPAN que ayudan con esta tarea. Consulte Config::Any.

Si desea crear estructuras de datos para cargar a través de eval (lo cual no es realmente una buena idea), Data::Dumper genera el código perl necesario para crear cualquier estructura de datos que le suministre. La razón principal por la que lo menciono es que es mucho más útil como una herramienta de depuración que un serializador.

Ahora que que es atendido, si desea cargar un archivo y evaluarlo (de nuevo, no es la mejor idea en casi todos los casos), se debe buscar en do o require.

my $stuff = do 'address.pl'; 

Pero no hagas eso. String eval es una herramienta que generalmente no se usa. El 99% del tiempo, si está planeando usar string eval, pare y piense en otra forma de resolver el problema. Do es una evaluación implícita, por lo que también cuenta.

Perl te ofrece muchas herramientas para hacer magia arriesgada y poderosa. Una gran parte de convertirse en una hábil programación de Perl radica en comprender qué cosas son riesgosas, por qué y cuándo tiene sentido usarlas. No espere que Perl lo mande con vallas y puertas para mantenerlo a salvo. En serio, considere recoger una copia de Effective Perl Programming o Perl Best Practices. Como novato, mucho le pasará por la cabeza al leer la primera vez, pero cualquiera de los dos puede ser una gran referencia a medida que crece y aprende.

Siguiente tema, ¿qué demonios estás tratando de lograr con todas esas citas escapadas? ¡Me duele la cabeza mirar esas cosas! Perl tiene some very, very nice quoting operators que puede utilizar para evitar tener que perder el tiempo con las comillas de escape en sus cadenas literales.

La => o coma de grasa indica automáticamente su lado izquierdo (LHS) como si fuera solo alfanumérico. Pero poner todas las comillas y escapes hace que las cosas sean realmente dudosas.

Cuando dice \'address\' => {}, Perl ve esto como \, el operador "obtener referencia" aplicado a un literal de cadena. En este caso, un literal de cadena sin terminar, porque nunca ofrece un ' sin escollas después del primero.

Si su objetivo es utilizar 'address', citas y todo como su clave hash, se puede hacer esto:

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

Si no desea que las cotizaciones, lo que parece un caso de uso mucho más habitual, simplemente hazlo:

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

¡Activa los mensajes de error que recibías! Perl tiene algunos mensajes de error bastante agradables una vez que aprendes lo que significan todos. Hasta entonces, puede ser un poco difícil entender su significado. Afortunadamente, Perl se envía con un script llamado splain: una útil herramienta útil que explicará los mensajes de error con mucho más detalle. También puede usar el módulo diagnostics para obtener automáticamente los mismos mensajes de error ampliados.

Ahora, si yo estaba escribiendo esto me gustaría hacer algo en este sentido:

gen_schema_files.pl - Un archivo para escribir archivos de esquema JSON. Podría editar manualmente sus esquemas si lo desea. Es posible que también desee configurar la salida para que sea más bonita si desea mejorar la legibilidad.

#!/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 - este es el código que carga los esquemas y hace cosas. El mío solo se carga. No tengo idea de lo que está haciendo con los datos ...

#!/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; 
} 

que he pasado por alto algunas cosas, pero he tratado de dejar comentarios con suficiente cantidad de los términos (vocabulario o incluso la jerga, si te gusta) para permitirte hacer búsquedas rentables.

El código anterior tiene un par de defectos. No hace ninguna comprobación de inclusiones circulares (funcionará durante un tiempo prolongado y eventualmente llenará la memoria y se bloqueará, no tan bien). La elección de la clave mágica puede no ser buena. Y probablemente haya más en los que aún no he pensado.

Perldoc es un recurso increíble, pero hay tantas cosas que lleva un tiempo aprender a encontrar cosas. Eche un vistazo a Perl Data Structures Cookbook y Arrays of Arrays tutorial. Como principiante, encontré que el Perl Functions by Category section of perlfunc fue increíblemente útil.

Creo que voy a parar, ahora que he escrito texto más que suficiente para cegar a la persona promedio. Espero que encuentres útil esta disertación. Bienvenido, una vez más, y buenas noches (por favor, ajuste a lo que sea su hora local cuando encuentre esta respuesta).