perl の Autovivification に頭を悩ませようとしていて、それがどのように聞こえるかに基づいて、動的オブジェクトには実行時まで型が割り当てられないため、C# のダイナミクスと同様に機能するようです。もしそうなら、私がC#で橋渡しできる、意味のある同等のアイデアはありますか?
編集
わかりましたので、どうやら私は道を外れているようです。2 部構成の質問の 2 部目として、概念的に C# に匹敵するものはありますか? 明確にするために、Autovivification に匹敵する C# の概念を探しています。まったく同じである必要はありませんが、意味をなすために概念的に十分に近いものです。先に述べたように、私はどう考えても perl ハッカーや Python ハッカーではありませんが、C ベースの言語である C、C++、C#、java、javascript には精通しています。私はC#のダイナミクスを考えていましたが、今のところ、ここの情報に基づいて遅延読み込みを考えています。
6 に答える
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 は の右側->
を見て、角括弧を確認します。これは、invocant$var
が配列参照でなければならないことを意味します。invocant が定義されていないため、Perl は新しい配列を作成し、その参照を にインストールします$var
。この同じプロセスが、その後の参照解除ごとに繰り返されます。
したがって、上記の行は実際には次のことを意味します。
(((($var //= [])->[1] //= {})->{x} //= [])->[3] //= {})->{asdf} ;
これはかなり恐ろしいことであり、したがって自動生存です。(//=
は perl 5.10+ の defined-or 代入演算子です)
アップデート:
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
したがって、基本的には、配列とハッシュの添え字の両方 (または同等のもの) を使用したインデックス作成をサポートするオブジェクトを作成する機能と、オブジェクトがメモリ内の自分自身を別のオブジェクトに置き換えることができる (またはそれ自体をロックできる) メカニズムの 2 つが必要です。 1 つの解釈を行い、新しいオブジェクトを内部に保存します。
次の疑似コードのようなものが出発点になる可能性があります。
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}
}
Uri Guttman のautovivification チュートリアルが役立つかもしれません。
基本的に、これは、これまで手付かずの集合体と集合体のメンバーが最初の使用時に生き返る能力です。
たとえば、私はこれを行うことができます:
#!/usr/bin/perl
use strict; use warnings;
use Data::Dumper;
my @dummy;
push @{ $dummy[0] }, split ' ', 'this that and the other';
push @{ $dummy[1] }, { qw(a b c d) };
print Dumper \@dummy;
逆参照される前はどちら$dummy[0]
も$dummy[1]
存在しません。
さて、あなたが放棄したいのであればstrict
(そうすべきではありません)、次のようなこともできます:
use Data::Dumper;
@$x = qw(a b c d);
print Dumper $x;
これにより、未定義の変数$x
は逆参照されるため、配列参照になります。
設定されていないキーへの が発生したときに、新しい (たとえば、再帰的に同じ型の)IDictionary<X,Y>
を返す (および保存する) などの作成により、autovification のような動作を実装できます。このアプローチは Ruby で使用され、大きな成功を収めています (例)。ただし、静的に型付けされた言語ではあまり役に立ちません。葉の値をきれいに「取得」する方法がないためです。少なくとも既存のほとんどのコンテキストではなどの契約。IDictionary<X,Y>
[]
IDictionary
の出現によりdynamic
、これは C# で正常に実行できる可能性がありますが、私にはわかりません。
C# での辞書の自動有効化のような動作の単純な実装については、このようなものはどうですか? 明らかに、これは Perl が行う一般的な方法では処理しませんが、同じ効果があると信じています。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
// The purpose of this class is to provide a dictionary with auto-vivification behaviour similar to Perl's
// Using dict[index] will succeed regardless of whether index exists in the dictionary or not.
// A default value can be set to be used as an initial value when the key doesn't exist in the dictionary
namespace XMLTest
{
class AutoDictionary<TKey,TValue> : Dictionary<TKey,TValue> {
Object DefaultValue ;
public AutoDictionary(Object DefaultValue) {
this.DefaultValue = DefaultValue;
}
public AutoDictionary() {
this.DefaultValue = null;
}
public new TValue this[TKey index] {
get {
try {
return base[index];
}
catch (KeyNotFoundException) {
base.Add(index, (TValue)DefaultValue);
return (TValue)DefaultValue ;
}
}
set {
try {
base[index] = value ;
}
catch (KeyNotFoundException) {
base.Add(index, value);
}
}
}
}
}
継承の代わりに拡張メソッドを使用することをお勧めします。
例えば:
namespace DictionaryEx
{
public static class Ex
{
public static TV Vivify<TK, TV>(this IDictionary<TK, TV> dict, TK key)
{
var value = default(TV);
if (dict.TryGetValue(key, out value))
{
return value;
}
value = default(TV);
dict[key] = value;
return value;
}
public static TV Vivify<TK, TV>(this IDictionary<TK, TV> dict, TK key, TV defaultValue)
{
TV value;
if (dict.TryGetValue(key, out value))
{
return value;
}
dict[key] = defaultValue;
return defaultValue;
}
public static TV Vivify<TK, TV>(this IDictionary<TK, TV> dict, TK key, Func<TV> valueFactory)
{
TV value;
if (dict.TryGetValue(key, out value))
{
return value;
}
value = valueFactory();
dict[key] = value;
return value;
}
}
}
インデクサーと 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"]);