En Perl, ¿cómo creo un hash cuyas claves provienen de una matriz determinada?

StackOverflow https://stackoverflow.com/questions/95820

  •  01-07-2019
  •  | 
  •  

Pregunta

Digamos que tengo una matriz, y sé que voy a hacer mucho "la matriz contiene X?" cheques.La forma eficiente de hacer esto es convertir esa matriz en un hash, donde las claves son los elementos de la matriz, y luego puedes simplemente decir

if($hash{X}) { ... }

¿Existe una manera sencilla de realizar esta conversión de matriz a hash?Idealmente, debería ser lo suficientemente versátil como para tomar una matriz anónima y devolver un hash anónimo.

¿Fue útil?

Solución

%hash = map { $_ => 1 } @array;

No es tan breve como las soluciones "@hash{@array} = ...", pero esas requieren que el hash y la matriz ya estén definidos en otro lugar, mientras que ésta puede tomar una matriz anónima y devolver un hash anónimo.

Lo que esto hace es tomar cada elemento de la matriz y emparejarlo con un "1".Cuando esta lista de pares (clave, 1, clave, 1, clave 1) se asigna a un hash, los impares se convierten en las claves del hash y los pares se convierten en los valores respectivos.

Otros consejos

 @hash{@array} = (1) x @array;

Es un segmento hash, una lista de valores del hash, por lo que tiene la lista-y @ al frente.

De los documentos:

Si está confundido sobre por qué usa un '@' allí en una porción hash en lugar de un '%', piense en ello así.El tipo de soporte (cuadrado o rizado) gobierna si se trata de una matriz o un hash.Por otro lado, el símbolo principal ('$' o '@') en la matriz o hash indica si está recuperando un valor singular (un escalar) o uno plural (una lista).

@hash{@keys} = undef;

La sintaxis aquí donde te refieres al hash con un @ es una rodaja de hachís.Básicamente estamos diciendo $hash{$keys[0]} Y $hash{$keys[1]} Y $hash{$keys[2]} ...Hay una lista en el lado izquierdo de =, un valor l, y lo asignamos a esa lista, que en realidad va al hash y establece los valores para todas las claves nombradas.En este caso, sólo especifiqué un valor, por lo que ese valor va en $hash{$keys[0]}, y las otras entradas hash se vivifican automáticamente (cobran vida) con valores indefinidos.[Mi sugerencia original aquí fue establecer la expresión = 1, lo que habría establecido esa clave en 1 y las demás en undef.Lo cambié por coherencia, pero como veremos a continuación, los valores exactos no importan.]

Cuando te des cuenta de que el valor l, la expresión en el lado izquierdo de =, es una lista construida a partir del hash, entonces comenzará a tener algún sentido por qué estamos usando eso. @.[Excepto que creo que esto cambiará en Perl 6.]

La idea aquí es que estés usando el hash como un conjunto.Lo que importa no es el valor que le estoy asignando;es sólo la existencia de las claves.Entonces lo que quieres hacer no es algo como:

if ($hash{$key} == 1) # then key is in the hash

en cambio:

if (exists $hash{$key}) # then key is in the set

En realidad, es más eficiente simplemente ejecutar un exists check que molestarse con el valor en el hash, aunque para mí lo importante aquí es solo el concepto de que estás representando un conjunto solo con las claves del hash.Además, alguien señaló que al usar undef como valor aquí, consumiremos menos espacio de almacenamiento del que consumiríamos asignando un valor.(Y también generar menos confusión, ya que el valor no importa, y mi solución asignaría un valor solo al primer elemento del hash y dejaría los demás undef, y algunas otras soluciones están dando vueltas para crear una serie de valores que se incluirán en el hash;esfuerzo completamente desperdiciado).

Tenga en cuenta que si escribe if ( exists $hash{ key } ) Si no te resulta demasiado trabajo (lo cual prefiero usar ya que el asunto de interés es en realidad la presencia de una clave en lugar de la veracidad de su valor), entonces puedes usar el breve y sencillo

@hash{@key} = ();

Aquí hay una presuposición, que la forma más eficiente de hacer mucho "la matriz contiene X?" Los cheques son para convertir la matriz a un hash.La eficiencia depende de recursos escasos, a menudo tiempo, pero a veces espacio y, a veces, esfuerzo del programador.Al menos estás duplicando la memoria consumida al mantener una lista y un hash de la lista simultáneamente.Además, estás escribiendo más código original que necesitarás probar, documentar, etc.

Como alternativa, mire el módulo List::MoreUtils, específicamente las funciones any(), none(), true() y false().Todos toman un bloque como condicional y una lista como argumento, similar a map() y grep():

print "At least one value undefined" if any { !defined($_) } @list;

Ejecuté una prueba rápida, cargando la mitad de /usr/share/dict/words en una matriz (25000 palabras), luego busqué once palabras seleccionadas de todo el diccionario (cada 5000 palabras) en la matriz, usando tanto la matriz método -to-hash y el any() función de List::MoreUtils.

En Perl 5.8.8 creado desde el código fuente, el método array-to-hash se ejecuta casi 1100 veces más rápido que el any() método (1300 veces más rápido en el paquete Perl 5.8.7 de Ubuntu 6.06).

Sin embargo, esa no es la historia completa: la conversión de matriz a hash tarda aproximadamente 0,04 segundos, lo que en este caso reduce la eficiencia del tiempo del método de matriz a hash a 1,5x-2x más rápido que el any() método.Sigue siendo bueno, pero no tan estelar.

Mi intuición es que el método array-to-hash va a superar any() en la mayoría de los casos, pero me sentiría mucho mejor si tuviera algunas métricas más sólidas (muchos casos de prueba, análisis estadísticos decentes, tal vez algún análisis algorítmico de gran O de cada método, etc.) Dependiendo de sus necesidades, enumere ::MoreUtils puede ser una mejor solución;ciertamente es más flexible y requiere menos codificación.Recuerde, la optimización prematura es un pecado...:)

Siempre pensé que

foreach my $item (@array) { $hash{$item} = 1 }

era al menos agradable y legible/mantenible.

En Perl 5.10, existe el casi mágico operador ~~:

sub invite_in {
    my $vampires = [ qw(Angel Darla Spike Drusilla) ];
    return ($_[0] ~~ $vampires) ? 0 : 1 ;
}

Mira aquí: http://dev.perl.org/perl5/news/2007/perl-5.10.0.html

También vale la pena señalar para completar, mi método habitual para hacer esto con 2 matrices de la misma longitud @keys y @vals que preferirías que fuera un hash...

my %hash = map { $keys[$_] => $vals[$_] } (0..@keys-1);

La solución de Raldi se puede ajustar a esto (el '=>' del original no es necesario):

my %hash = map { $_,1 } @array;

Esta técnica también se puede utilizar para convertir listas de texto en hashes:

my %hash = map { $_,1 } split(",",$line)

Además, si tiene una línea de valores como esta:"foo=1,bar=2,baz=3" puedes hacer esto:

my %hash = map { split("=",$_) } split(",",$line);

[EDITAR para incluir]


Otra solución ofrecida (que requiere dos líneas) es:

my %hash;
#The values in %hash can only be accessed by doing exists($hash{$key})
#The assignment only works with '= undef;' and will not work properly with '= 1;'
#if you do '= 1;' only the hash key of $array[0] will be set to 1;
@hash{@array} = undef;

También podrías usar Perl6::Unión.

use Perl6::Junction qw'any';

my @arr = ( 1, 2, 3 );

if( any(@arr) == 1 ){ ... }

Si realiza muchas operaciones teóricas de conjuntos, también puede usar Conjunto::Escalar o módulo similar.Entonces $s = Set::Scalar->new( @array ) construirá el conjunto por usted y podrá consultarlo con: $s->contains($m).

Puede colocar el código en una subrutina, si no quiere contaminar su espacio de nombres.

my $hash_ref =
  sub{
    my %hash;
    @hash{ @{[ qw'one two three' ]} } = undef;
    return \%hash;
  }->();

O mejor:

sub keylist(@){
  my %hash;
  @hash{@_} = undef;
  return \%hash;
}

my $hash_ref = keylist qw'one two three';

# or

my @key_list = qw'one two three';
my $hash_ref = keylist @key_list;

Si realmente quisieras pasar una referencia de matriz:

sub keylist(\@){
  my %hash;
  @hash{ @{$_[0]} } = undef if @_;
  return \%hash;
}

my @key_list = qw'one two three';
my $hash_ref = keylist @key_list;

Es posible que también desees consultar Corbata::IxHash, que implementa matrices asociativas ordenadas.Eso le permitiría realizar ambos tipos de búsquedas (hash e índice) en una copia de sus datos.

#!/usr/bin/perl -w

use strict;
use Data::Dumper;

my @a = qw(5 8 2 5 4 8 9);
my @b = qw(7 6 5 4 3 2 1);
my $h = {};

@{$h}{@a} = @b;

print Dumper($h);

da (tenga en cuenta que las claves repetidas obtienen el valor en la posición más grande de la matriz, es decir, 8->2 y no 6)

$VAR1 = {
          '8' => '2',
          '4' => '3',
          '9' => '1',
          '2' => '5',
          '5' => '4'
        };
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top