Articulo de referencia

Autovivificación

En el lenguaje de programación Perl , la autovivificación es la creación automática de nuevos arreglos y hashes según sea necesario cada vez que se desreferencia un valor indefi...

En el lenguaje de programación Perl , la autovivificación es la creación automática de nuevos arreglos y hashes según sea necesario cada vez que se desreferencia un valor indefinido . La autovivificación de Perl permite a un programador referirse a una variable estructurada y a subelementos arbitrarios de esa variable estructurada, sin declarar expresamente la existencia de la variable y su estructura completa de antemano. [ 1 ]

Por el contrario, otros lenguajes de programación:

  1. Exigir que un programador declare expresamente una estructura de variables completa antes de usar o hacer referencia a cualquier parte de ella; o
  2. Exigir que un programador declare una parte de una estructura de variables antes de hacer referencia a cualquier parte de ella; o
  3. Cree una asignación a una parte de una variable antes de referirse a ella, asignarle un valor o componer una expresión que haga referencia a cualquier parte de la misma.

La autovivificación de Perl se puede contrastar con lenguajes como Python , PHP , Ruby y muchos lenguajes de estilo C, donde generalmente no se permite la desreferenciación de valores nulos o indefinidos. [ a ] ​​Se puede comparar con el "acceso con nombre en el objeto window" del estándar HTML [ 2 ] que da como resultado que las variables correspondientes de ámbito global sean automáticamente accesibles para JavaScript basado en el navegador .

Hashes

Es importante recordar que la autovivificación ocurre cuando se desreferencia un valor indefinido. No es necesaria una asignación. La siguiente sesión de depuración ilustra la autovivificación de un hash simplemente examinándolo:

DB <1> x \ %h 0 HASH ( 0x2f1a248 ) hash vacío DB <2> x $h { 1 }{ 2 }{ 3 }{ 4 } 0 indefinido DB <3> x \ %h 0 HASH ( 0x2f1a248 ) 1 => HASH ( 0x2f1a260 ) 2 => HASH ( 0x29a3c68 ) 3 => HASH ( 0x2dc3038 ) hash vacío DB <4>

La sesión de depuración que se muestra a continuación ilustra la autovivificación de un hash a partir de la asignación a un hash interno:

DB <1> $h { A }{ B }{ C }{ D } = 1 DB <2> x \ %h 0 HASH ( 0x83c71ac ) 'A' => HASH ( 0x837d50c ) 'B' => HASH ( 0x83c71e8 ) 'C' => HASH ( 0x83c7218 ) 'D' => 1 DB <3>

Se crearon hashes de varias capas de profundidad automáticamente sin ninguna declaración. La autovivificación puede evitar la escritura excesiva. Si Perl no admitiera la autovivificación, la estructura anterior tendría que crearse de la siguiente manera:

DB <1> %h = ( A => { B => { C => { D => 1 }}}) DB <2> x \ %h 0 HASH ( 0x83caba4 ) 'A' => HASH ( 0x83cfc28 ) 'B' => HASH ( 0x83cab74 ) 'C' => HASH ( 0x83b6110 ) 'D' => 1 DB <3>

Identificadores de archivos y directorios

Perl 5.6.1 y versiones posteriores admiten la autovivificación de identificadores de archivos y directorios. [ 3 ] Llamar open()a una variable indefinida la establecerá en un identificador de archivo. Según perl561delta, "[e]sto elimina en gran medida la necesidad de patrones de tipo al abrir identificadores de archivo que deben pasarse, como en el siguiente ejemplo:

para mi $file ( qw(this.conf that.conf) ) { mi $fin = open_or_throw ( '<' , $file ); process_conf ( $fin ); # no se necesita close() } use Carp ; sub open_or_throw { mi ( $mode , $filename ) = @_ ; abre mi $h , $mode , $filename o croak "No se pudo abrir '$filename': $!" ; return $h ; }

Emulación en otros lenguajes de programación

C++

Los contenedores asociativos de la biblioteca estándar de C++std::unordered_map ( y std::map) se utilizan operator[]para obtener el valor asociado a una clave. Si no hay nada asociado a esta clave, la construirá e inicializará el valor [ 4 ] . Para tipos primitivos o trivialmente construibles como o , la inicialización del valor será una inicialización a cero.intfloat

using std :: string ; using std :: unordered_map ; using std :: vector ;// Un mapa hash de cadenas a una lista de enteros unordered_map < string , vector < int >> a ; a [ "answer" ]. push_back ( 42 ); // Autovivifica el vector a["answer"] y luego lo agrega.

Otro ejemplo de cómo contar ocurrencias de cadenas:

using std :: string ; using std :: unordered_map ;string nextString () { // Recupera una cadena (implementación omitida) }// Un mapa hash de cadenas a enteros unordered_map < string , int > counts ; while ( string & s = nextString ()) { counts [ s ] ++ ; // crea counts[s] si no existe y lo establece a cero, luego incrementa. }

Se puede lograr un truco similar con el insert()método, que devuelve un iterador al elemento asociado a la clave, incluso si ya existe.

Pitón

La clase integrada de Python dictpuede ser subclaseada para implementar diccionarios autovivificiosos simplemente sobrescribiendo el __missing__()método que se agregó a la clase en Python v2.5. [ 5 ] Hay otras formas de implementar el comportamiento, [ 6 ] [ 7 ] pero la siguiente es una de las más simples y las instancias de la clase imprimen igual que los objetos de diccionario normales de Python.

from typing import Any class Tree ( dict ): def __missing__ ( self , key : Any ) -> Any : value : Any = self [ key ] = type ( self )() return value# Nombres comunes por clase, orden, género y especie tipo common_names : Árbol = Árbol () common_names [ 'Mammalia' ][ 'Primates' ][ 'Homo' ][ 'H. sapiens' ] = 'ser humano' print ( common_names ) # {'Mammalia': {'Primates': {'Homo': {'H. sapiens': 'ser humano'}}}}# Citas famosas por obra, acto, escena y página : Tree = Tree () quotes [ 'Hamlet' ] [ 1 ][ 3 ][ 3 ] = 'Sobre todo esto: sé fiel a ti mismo.' print ( quotes ) # {'Hamlet': {1: {3: {3: 'Sobre todo esto: sé fiel a ti mismo.'}}}}

Rubí

Los hashes de Ruby pueden aceptar un bloque que especifique el objeto que se devolverá para los índices inexistentes. Estos se pueden usar para implementar mapas autovivificiosos.

irb(main):001:0> árbol = proc { Hash . nuevo { | hash , clave | hash [ clave ] = árbol . llamada } } => #<Proc:0x007fda528749a0@(irb):1> irb(main):002:0> lupin = árbol . llamada => {} irb(main):003:0> lupin [ "express" ][ 3 ] = "stand and deliver" => "stand and deliver" irb(main):004:0> lupin => {"express"=>{3=>"stand and deliver"}}

Java

Java Map tiene un método computeIfAbsent[ 8 ] que se puede utilizar para emular mapas autovivificantes.

import java.util.ArrayList ; import java.util.HashMap ; import java.util.List ; import java.util.Map ; import java.util.function.Function ; import java.util.function.Supplier ;public static < K , V > Function < K , V > defaultDict ( Map < K , V > map , Supplier <? extends V > supplier ) { return key -> map . computeIfAbsent ( key , k -> supplier . get ()); }public static void main ( String [] args ) { Function < String , List < String >> dict = defaultDict ( new HashMap <> (), ArrayList :: new ); dict . apply ( "foo" ). add ( "bar" ); }

PHP

Los arrays de PHP son autovivificiosos por naturaleza.

$arr = array (); $arr [ "express" ][ 3 ] = "stand and deliver" ;

Sin embargo, esto solo se aplica a la asignación, y no al acceso a matrices.

JavaScript

ES6 introduce una nueva clase Proxy que se puede usar para implementar la autovivificación. Con otras características de JavaScript, esto se puede reducir a una sola línea de código:

let tree = () => new Proxy ({}, { get : ( target , name ) => name in target ? target [ name ] : target [ name ] = tree () });// Prueba: let t = tree (); t . first . second . third = 'text' ; console . log ( t . first . second . third ); // o t['first']['second']['third']

DO#

C# , utilizando indexadores y dinámica de C# 4.0,

using System ; using System.Collections.Generic ;clase Árbol { privado IDictionary < string , object > _dict = new Dictionary < string , object > ();public dynamic this [ string key ] { get { return _dict.ContainsKey ( key ) ? _dict [ key ] : _dict [ key ] = new Tree ( ) ; } set { _dict [ key ] = value ; } } }// Prueba: Árbol t = nuevo (); t [ "primero" ][ "segundo" ][ "tercero" ] = "texto" ; Console . WriteLine ( t [ "primero" ][ "segundo" ][ "tercero" ]);

DynamicObjectTambién se puede utilizar para implementar diferentes sintaxis.

using System ; using System.Collections.Generic ; using System.Dynamic ;clase Árbol : Objeto Dinámico { privado IDictionary < objeto , objeto > dict = nuevo Dictionary < objeto , objeto > ();// para la sintaxis t.first.second.third public override bool TryGetMember ( GetMemberBinder binder , out object result ) { string key = binder . Name ;if ( dict.ContainsKey ( key ) ) { result = dict [ key ]; } else { dict [ key ] = result = new Tree ( ); }return true ; } public override bool TrySetMember ( SetMemberBinder binder , object value ) { dict [ binder.Name ] = value ; return true ; }// para la sintaxis t["first"]["second"]["third"] public override bool TryGetIndex ( GetIndexBinder binder , object [] indexes , out object result ) { object key = indexes [ 0 ];if ( dict.ContainsKey ( key ) ) { result = dict [ key ]; } else { dict [ key ] = result = new Tree ( ); }devolver verdadero ; }public override bool TrySetIndex ( SetIndexBinder binder , object [] indexes , object value ) { dict [ indexes [ 0 ]] = value ; return true ; } }// Prueba: dynamic t = new Tree (); t . first . second . third = "text" ; Console . WriteLine ( t . first . second . third );// o bien, dynamic t = new Tree (); t [ "first" ][ "second" ][ "third" ] = "text" ; Console . WriteLine ( t [ "first" ][ "second" ][ "third" ]);

Véase también

Notas

  1. Por ejemplo, Python genera un TypeError si se llama a None.__getitem__. Desreferenciar un puntero nulo en C produce un comportamiento indefinido; muchas implementaciones de C optan por generar un error de segmentación .

Referencias

  1. Schwartz, Randal L.; Phoenix, Tom (2003). Aprendiendo objetos Perl . O'Reilly Media, Inc. pág . 42. ISBN  9780596004781Este proceso se denomina autovivificación . Cualquier variable inexistente, o una variable que contenga `undef`, que se desreferencia mientras se busca una ubicación de variable (técnicamente llamada contexto lvalue), se rellena automáticamente con la referencia apropiada a un elemento vacío...
  2. "Estándar HTML. Acceso con nombre al objeto Window" .
  3. "perl561delta - novedades de perl v5.6.1" . Documentación de programación de Perl .
  4. "Inicialización de valores", referencia de C++ (wiki)
  5. "Tipos de mapeo — dict" . Consultado el 13 de junio de 2016 .
  6. "¿Cuál es la mejor manera de implementar diccionarios anidados en Python?" . Consultado el 13 de junio de 2016 .
  7. "Árbol de una línea en Python" . Consultado el 27 de diciembre de 2017 .
  8. "Mapa (Plataforma Java SE 8)" . Consultado el 17 de mayo de 2015 .
  • perl561delta: Los identificadores de archivos y directorios se pueden autoactivar.
  • Autovivificación en Perl: Un tutorial en profundidad
  • Autovivificación en Ruby : emulación de la autovivificación de Perl
  • Uso del Combinador Y en Ruby : Implementa la autovivificación en Ruby con el Combinador Y.
  • Hash#autonew en la gema Ruby "facets" agrega autovivificación en lecturas de hash.
  • La gema Ruby "xkeys" facilita el recorrido de estructuras anidadas y se activa automáticamente al escribir en matrices o hashes.