Я не могу говорить с C #, но с точки зрения непрофессионала, автовификация Perl - это процесс создания объекта контейнера из неопределенного значения, как только это необходимо.
Несмотря на то, что большая часть Perl довольно динамична, синтаксис разыменования Perl однозначно определяет тип ссылки во время компиляции. Это позволяет интерпретатору узнать, что ему нужно из переменной, еще до того, как переменная будет определена.
my $var; # undefined
# to autovivify to an array:
@$var = 1..5; # @ here implies ARRAY
$$var[4] = 5; # square brackets imply ARRAY
$#$var; # $# implies ARRAY (returns the last index number)
# to autovivify to a hash:
%$var = (a => 1); # % implies HASH
$$var{asdf} = 5; # curly braces imply HASH
Этот список может быть длиннее, но он должен дать вам представление.
В общем, когда у вас есть такая строка:
my $var;
$var->[1]{x}[3]{asdf}
Perl смотрит на правую сторону ->
и видит квадратные скобки. Это означает, что инвокант $var
должен быть ссылкой на массив. Поскольку инвокант не определен, Perl создает новый массив и устанавливает его ссылку в $var
. Затем этот же процесс повторяется для каждой последующей разыменования.
Таким образом, строка выше действительно означает:
(((($var //= [])->[1] //= {})->{x} //= [])->[3] //= {})->{asdf};
, что довольно отвратительно, и, следовательно, автовивификации. (//=
является оператором определения или присваивания в perl 5.10 +)
Обновление:
В соответствии с комментарием cjm, чтобы выразить это в общих не-perl терминах, чтобы добиться автовивификации на другом языке, вам нужен ленивый объект, который поддерживает индексацию через [...]
и {...}
. Когда выполняется любая из этих операций индексации, объект заменяет себя либо массивом, либо хэшем. При каждом доступе к объекту, если ячейка пуста, он должен возвращать другой ленивый объект.
obj = new lazy_obj()
level1 = obj[4] # sets obj to be an array, returns a new lazy_obj for level1
level2 = level1{asdf} # sets level1 (and obj[4]) to a hash,
# returns a new lazy_obj for level2
Таким образом, в основном вам нужны две вещи: возможность создать объект, который поддерживает индексирование с помощью индексов массива и хеша (или эквивалентного), и механизм, позволяющий объекту заменять себя в памяти другим объектом (или который может привязать себя к одной интерпретации, а затем сохранить новый объект внутри.
Что-то вроде следующего псевдокода может быть началом:
class autoviv {
private var content;
method array_subscript (idx) {
if (!content) {
content = new Array();
}
if (typeof content == Array) {
if (exists content[idx]) return content[idx];
return content[idx] = new autoviv();
} else {
throw error
}
}
method hash_subscript (idx) {
if (!content) {
content = new Hash();
}
if (typeof content == Hash) {
if (exists content{idx}) return content{idx};
return content{idx} = new autoviv();
} else {
throw error
}
}
// overload all other access to return undefined, so that the value
// still looks empty for code like:
//
// var auto = new autoviv();
// if (typeof auto[4] == autoviv) {should run}
// if (auto[4]) {should not run}
}