Автовивификация
В Perl языке программирования автовивификация — это автоматическое создание новых массивов и хешей неопределенное значение по мере необходимости каждый раз, когда разыменовывается . Автовивификация Perl позволяет программисту ссылаться на структурированную переменную и произвольные подэлементы этой структурированной переменной без предварительного явного объявления существования переменной и ее полной структуры. [1]
Напротив, другие языки программирования либо: 1) требуют от программиста явного объявления всей структуры переменных перед использованием или ссылкой на любую ее часть; или 2) требовать от программиста объявления части переменной структуры перед обращением к любой ее части; или 3) создать присвоение части переменной перед обращением, присвоением или составлением выражения, которое ссылается на любую ее часть.
Автовивификацию Perl можно сравнить с такими языками, как Python , PHP , Ruby и многими языками стиля C, где разыменование нулевых или неопределенных значений обычно не допускается. [а] Его можно сравнить с «именованным доступом к объекту окна» стандарта HTML. [2] в результате чего соответствующие глобальные переменные автоматически становятся доступными для браузерного JavaScript .
Хэши
[ редактировать ]Важно помнить, что автовивификация происходит при разыменовании неопределенного значения. Задание не обязательно. Приведенный ниже сеанс отладчика иллюстрирует автоматическое оживление хэша после его проверки:
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>
Приведенный ниже сеанс отладчика иллюстрирует автоматическое оживление хеша при присвоении его внутреннему хешу:
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>
Хэши глубиной в несколько слоев создавались автоматически, без каких-либо объявлений. Автовивификация может предотвратить чрезмерный ввод текста. Если бы Perl не поддерживал автовивификацию, приведенную выше структуру пришлось бы создавать следующим образом:
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>
Дескрипторы файлов и каталогов
[ редактировать ]Perl 5.6.1 и новее поддерживают автоактивацию дескрипторов файлов и каталогов. [3] Вызов open()
для неопределенной переменной будет установлен дескриптор файла. Согласно perl561delta, «[t]his в значительной степени устраняет необходимость в typeglobs при открытии дескрипторов файлов, которые необходимо передавать, как в следующем примере:
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;
}
Эмуляция на других языках программирования
[ редактировать ]С++
[ редактировать ]Ассоциативные контейнеры стандартной библиотеки C ++ ( std::unordered_map
и std::map
) использовать operator[]
чтобы получить значение, связанное с ключом. Если с этим ключом ничего не связано, он создаст его и инициализирует значение.
[4]
ценность. Для простых типов, таких как int или float, инициализация значения будет нулевой.
std::unordered_map<std::string, std::vector<int>> a;
a["the answer"].push_back(42); // Autovivifies the a["the answer"] vector and then appends to it.
Другой пример подсчета вхождений строк:
std::unordered_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.
}
Аналогичного трюка можно добиться с помощью insert()
метод, который возвращает итератор к элементу, связанному с ключом, даже если он уже существует.
Питон
[ редактировать ]встроенный в Python dict
класс может быть подклассом для реализации автоживых словарей, просто переопределив __missing__()
метод, который был добавлен в класс в Python v2.5. [5] Существуют и другие способы реализации поведения, [6] [7] но следующий пример — один из самых простых, и экземпляры класса печатаются так же, как и обычные объекты словаря Python.
>>> class Tree(dict):
... def __missing__(self, key):
... value = self[key] = type(self)()
... return value
>>> # Common names by class, order, genus, and type-species
>>> common_names = Tree()
>>> common_names['Mammalia']['Primates']['Homo']['H. sapiens'] = 'human being'
>>> common_names
{'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.'
>>> quotes
{'Hamlet': {1: {3: {3: 'This above all: to thine own self be true.'}}}}
Руби
[ редактировать ]Хэши Ruby могут принимать блок, определяющий возвращаемый объект для несуществующих индексов. Их можно использовать для реализации автоживущих карт.
irb(main):001:0> tree = proc { Hash.new { |hash, key| hash[key] = tree.call } }
=> #<Proc:0x007fda528749a0@(irb):1>
irb(main):002:0> lupin = tree.call
=> {}
irb(main):003:0> lupin["express"][3] = "stand and deliver"
=> "stand and deliver"
irb(main):004:0> lupin
=> {"express"=>{3=>"stand and deliver"}}
Ява
[ редактировать ]Java Map имеет метод computeIfAbsent
[8] который можно использовать для эмуляции автоживущих карт.
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 изначально являются автоживыми.
$arr = array();
$arr["express"][3] = "stand and deliver";
Однако это относится только к присваиванию, а не к доступу к массиву.
JavaScript
[ редактировать ]ES6 представляет новый Прокси- класс, который можно использовать для реализации автовивификации. Благодаря другим функциям JavaScript это можно свести к одной строке кода:
var tree = () => new Proxy({}, { get: (target, name) => name in target ? target[name] : target[name] = tree() });
// Test:
var t = tree();
t.first.second.third = 'text';
console.log(t.first.second.third); // or t['first']['second']['third']
С#
[ редактировать ]C#, используя индексаторы и динамику C# 4.0,
class Tree
{
private 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; }
}
}
// Test:
var t = new Tree();
t["first"]["second"]["third"] = "text";
Console.WriteLine(t["first"]["second"]["third"]);
DynamicObject также можно использовать для реализации различных синтаксисов.
using System;
using System.Collections.Generic;
using System.Dynamic;
class Tree : DynamicObject
{
private IDictionary<object, object> dict = new Dictionary<object, object>();
// for t.first.second.third syntax
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
var 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;
}
// for t["first"]["second"]["third"] syntax
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
var key = indexes[0];
if (dict.ContainsKey(key))
result = dict[key];
else
dict[key] = result = new Tree();
return true;
}
public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
{
dict[indexes[0]] = value;
return true;
}
}
// Test:
dynamic t = new Tree();
t.first.second.third = "text";
Console.WriteLine(t.first.second.third);
// or,
dynamic t = new Tree();
t["first"]["second"]["third"] = "text";
Console.WriteLine(t["first"]["second"]["third"]);
См. также
[ редактировать ]Примечания
[ редактировать ]- ^ Например, Python выдает ошибку TypeError, если вызывается None.__getitem__. Разыменование нулевого указателя в C приводит к неопределенному поведению; многие реализации C предпочитают выдавать ошибку сегментации .
Ссылки
[ редактировать ]- ^ Шварц, Рэндал Л.; Феникс, Том (2003). Изучение объектов Perl . О'Рейли Медиа, Инк. с. 42 . ISBN 9780596004781 .
Этот процесс называется аутовивификацией. Любая несуществующая переменная или переменная, содержащая undef, которая разыменовывается при поиске местоположения переменной (технически называемая контекстом lvalue), автоматически заполняется соответствующей ссылкой на пустой элемент...
- ^ «Стандарт HTML. Именованный доступ к объекту Window» .
- ^ «perl561delta — что нового в Perl v5.6.1» . Документация по программированию на Perl .
- ^ «Инициализация значения», справочник C++ (вики)
- ^ «Типы сопоставления — dict» . Проверено 13 июня 2016 г.
- ^ «Как лучше всего реализовать вложенные словари в Python?» . Проверено 13 июня 2016 г.
- ^ «Однострочное дерево в Python» . Проверено 27 декабря 2017 г.
- ^ «Карта (платформа Java SE 8)» . Проверено 17 мая 2015 г.
Внешние ссылки
[ редактировать ]- perl561delta: дескрипторы файлов и каталогов могут быть автоматически активизированы.
- Автовивификация в Perl: углубленное руководство
- Автовивификация в Ruby — эмуляция автовивификации Perl
- Использование Y-комбинатора в Ruby — реализует автовивификацию в Ruby с помощью Y-комбинатора.
- Hash#autonew в «фасетах» драгоценного камня Ruby добавляет автовивификацию при чтении хеша.
- Гем Ruby «xkeys» облегчает обход вложенных структур и автооживление при записи массива или хэша.