Autovivification
In the Perl programming language, autovivification is the automatic creation of new arrays and hashes as required every time an undefined value is dereferenced. Perl autovivification allows a programmer to refer to a structured variable, and arbitrary sub-elements of that structured variable, without expressly declaring the existence of the variable and its complete structure beforehand.[1]
In contrast, other programming languages either: 1) require a programmer to expressly declare an entire variable structure before using or referring to any part of it; or 2) require a programmer to declare a part of a variable structure before referring to any part of it; or 3) create an assignment to a part of a variable before referring, assigning to or composing an expression that refers to any part of it.
Perl autovivication can be contrasted against languages such as Python, PHP, Ruby, and many of the C style languages, where dereferencing null or undefined values is not generally permitted.[2] It can be compared to the HTML standard's "named access on the window object"[3] which results in corresponding globally scoped variables being automatically accessible to browser-based JavaScript.
Hashes
It is important to remember that autovivification happens when an undefined value is dereferenced. An assignment is not necessary. The debugger session below illustrates autovivification of a hash just from examining it:
DB<1> x \%h
0 HASH(0x2f1a248)
empty hash
DB<2> x $h{1}{2}{3}{4}
0 undef
DB<3> x \%h
0 HASH(0x2f1a248)
1 => HASH(0x2f1a260)
2 => HASH(0x29a3c68)
3 => HASH(0x2dc3038)
empty hash
DB<4>
The debugger session below illustrates autovivification of a hash from assigning to an inner hash:
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>
Hashes several layers deep were created automatically without any declarations. Autovivification can prevent excessive typing. If Perl did not support autovivification, the structure above would have to be created as follows:
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>
File and Directory Handles
Perl 5.6.1 and newer support autovivification of file and directory handles.[4] Calling open()
on an undefined variable will set it to a filehandle. According to perl561delta, "[t]his largely eliminates the need for typeglobs when opening filehandles that must be passed around, as in the following example:
for my $file ( qw(this.conf that.conf) ) {
my $fin = open_or_throw('<', $file);
process_conf( $fin );
# no close() needed
}
use Carp;
sub open_or_throw {
my ($mode, $filename) = @_;
open my $h, $mode, $filename
or croak "Could not open '$filename': $!";
return $h;
}
Emulation in other programming languages
C++
C++'s associative containers like std::map
use operator[]
to get the value associated to a key. If there is nothing associated to this key, it will construct it:
std::map<int, std::vector<int>> a;
a[1].push_back(42); // Autovivification of the a[1] vector.
Another example of counting occurrences of a string
std::map<std::string, int> counts;
while(auto& s = GetNextString()) {
counts[s]++; // creates counts[s] if it doesn't exist and set to zero, then increment.
}
A similar trick can be achieved with the insert()
method, which returns an iterator to the element associated to the key, even if it already exists.
Python
Python's collections
module contains a defaultdict
class[5] which can be used to implement autovivificious dictionaries via a relatively simple recursive function.[6][7]
from collections import defaultdict
def tree():
return defaultdict(tree)
# common name by class, order, genus, and type-species
common_name = tree()
common_name['Mammalia']['Primates']['Homo']['H. sapiens'] = 'human being'
# Famous quotes by play, act, scene, and page
quotes = tree()
quotes['Hamlet'][1][3][3] = 'This above all: to thine own self be true.'
Ruby
Ruby hashes can take a block specifying an object to be returned for non-existing indexes. These can be used to implement autovivificious maps.
tree = proc { Hash.new { |hash, key| hash[key] = tree.call } }
lupin = tree.call
lupin["express"][3] = "stand and deliver"
Java
Java Map has a method computeIfAbsent[8] that can be used to emulate autovivificous maps.
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
PHP arrays are natively autovivificious.
$arr = array();
$arr["express"][3] = "stand and deliver";
However, this only applies to assignment, and not array access.
See also
References
- ↑ Schwartz, Randal L.; Phoenix, Tom (2003). Learning Perl Objects. O'Reilly Media, Inc. p. 42. ISBN 9780596004781.
This process is called autovivification. Any nonexisting variable, or a variable containing undef, which is dereferenced while looking for a variable location (technically called an lvalue context), is automatically stuffed with the appropriate reference to an empty item...
- ↑ For example, Python raises an TypeError if None.__getitem__ is called. Dereferencing a null pointer in C results in undefined behavior; many C implementations choose to raise a segmentation fault.
- ↑ http://www.whatwg.org/specs/web-apps/current-work/#named-access-on-the-window-object
- ↑
- ↑ "collections - High-performance container datatypes - Python v2 documentation". Retrieved 2013-08-14.
- ↑ "What is the best way to implement nested dictionaries in Python?". Retrieved 2013-08-14.
- ↑ "One-line Tree in Python". Retrieved 2015-08-21.
- ↑ "Map (Java Platform SE 8)". Retrieved 2015-05-17.
External links
- perl561delta: File and directory handles can be autovivified
- Autovivification in Perl: An In-Depth Tutorial
- Autovivification in Ruby - emulate Perl's autovivification
- A Use of the Y Combinator in Ruby - Implements autovivification in Ruby with the Y Combinator.
- Hash#autonew in the Ruby gem "facets" adds autovivification on hash reads
- The Ruby gem "xkeys" facilitates nested structure traversal and autovivifies on array or hash writes