Perlプログラミング言語における自動活性化とは、未定義値が参照されるたびに、必要に応じて新しい配列とハッシュを自動的に作成することです。Perlの自動活性化により、プログラマーは構造化変数とその任意のサブ要素を参照することができ、変数の存在とその完全な構造を事前に明示的に宣言する必要はありません。[ 1 ]
対照的に、他のプログラミング言語では次のいずれかになります。
Perlの自動活性化は、 Python、PHP、Ruby、そして多くのCスタイル言語などの言語とは対照的です。これらの言語では、null値や未定義値の参照は一般的に許可されていません。 [ a ]これは、HTML標準の「ウィンドウオブジェクトへの名前付きアクセス」[ 2 ]と比較することができ、対応するグローバルスコープの変数がブラウザベースのJavaScriptから自動的にアクセスできるようになります。
自動有効化は未定義値が参照解除されたときに発生することを覚えておくことが重要です。代入は不要です。以下のデバッガセッションは、ハッシュを検査するだけで自動有効化が行われる様子を示しています。
DB <1> x \ %h 0 HASH ( 0x2f1a248 )空のハッシュ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 )空のハッシュDB <4>以下のデバッガー セッションは、内部ハッシュへの割り当てによるハッシュの自動有効化を示しています。
DB <1> $h { A }{ B }{ C }{ D } = 1 DB <2> x \ %h 0ハッシュ( 0x83c71ac ) 'A' =>ハッシュ( 0x837d50c ) 'B' =>ハッシュ( 0x83c71e8 ) 'C' =>ハッシュ( 0x83c7218 ) 'D' => 1 DB <3>ハッシュは、宣言なしで複数階層に自動的に作成されます。自動活性化は、過剰な型付けを防ぐことができます。Perlが自動活性化をサポートしていなかった場合、上記の構造は次のように作成する必要がありました。
DB <1> %h = ( A => { B => { C => { D => 1 }}}) DB <2> x \ %h 0ハッシュ( 0x83caba4 ) 'A' =>ハッシュ( 0x83cfc28 ) 'B' =>ハッシュ( 0x83cab74 ) 'C' =>ハッシュ( 0x83b6110 ) 'D' => 1 DB <3>Perl 5.6.1以降では、ファイルハンドルとディレクトリハンドルの自動有効化をサポートしています。[ 3 ]未定義の変数open()を呼び出すと、その変数はファイルハンドルに設定されます。perl561deltaによると、「これにより、次の例のように、ファイルハンドルを渡す必要がある場合に 型グロブを使用する必要性が大幅に軽減されます。」
for my $file ( qw(this.conf that.conf) ) { my $fin = open_or_throw ( '<' , $file ); process_conf ( $fin ); # close() は必要ありません} use Carp ; sub open_or_throw { my ( $mode , $filename ) = @_ ; open my $h , $mode , $filenameまたはcroak "'$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 ); // a["the answer"] ベクトルを自動有効化し、それに追加します。文字列の出現回数をカウントする別の例:
std :: unordered_map < std :: string , int > counts ; while ( auto & s = GetNextString ()) { counts [ s ] ++ ; // counts[s] が存在しない場合は作成し、ゼロに設定してから増分します。}同様のトリックは、キーに関連付けられた要素が既に存在する場合でも、その要素への 反復子insert()を返す メソッドでも実現できます。
Pythonの組み込みクラスは、Python v2.5でクラスに追加されたメソッドをオーバーライドするだけで、自動生成辞書を実装するようにサブクラス化dictできます。 [ 5 ]この動作を実装する方法は他にもありますが、[ 6 ] [ 7 ]以下は最も単純な方法の1つであり、クラスのインスタンスは通常のPython辞書オブジェクトと同じように印刷されます。 __missing__()
>>>クラスTree ( dict ): ... def __missing__ ( self , key ): ... value = self [ key ] = type ( self )() ...戻り値>>> # 綱、目、属、タイプ種による一般名>>> common_names = Tree () >>> common_names [ 'Mammalia' ][ 'Primates' ][ 'Homo' ][ 'H. sapiens' ] = 'human being' >>> common_names {'Mammalia': {'Primates': {'Homo': {'H. sapiens': 'human being'}}}}>>> # 演劇、幕、シーン、ページ別の有名な引用>>> quotes = Tree () >>> quotes [ 'ハムレット' ][ 1 ][ 3 ][ 3 ] = '何よりもこれ: 汝自身の心に忠実であれ。' >>> quotes {'ハムレット': {1: {3: {3: '何よりもこれ: 汝自身の心に忠実であれ。'}}}}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 ]がある。
パブリック静的< 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 配列はネイティブで自動有効化されます。
$arr = array (); $arr [ "express" ][ 3 ] = "stand and deliver" ;ただし、これは割り当てにのみ適用され、配列アクセスには適用されません。
ES6では、自動有効化を実装できる 新しいProxyクラスが導入されました。JavaScriptの他の機能と組み合わせることで、この処理は1行のコードに短縮できます。
var tree = () => new Proxy ({}, { get : ( target 、name ) => name in target ? target [ name ] : target [ name ] = tree () });// テスト: var t = tree (); t . first . second . third = 'text' ; console . log ( t . first . second . third ); // または t['first']['second']['third']C#、インデクサーとC# 4.0ダイナミクスを使用
クラスTree { private IDictionary <文字列、オブジェクト> _dict = new Dictionary <文字列、オブジェクト> ();public dynamic this [ string key ] { get { return _dict . ContainsKey ( key ) ? _dict [ key ] : _dict [ key ] = new Tree (); } set { _dict [ key ] = value ; } } }//テスト: var t = new Tree (); t [ " first" ][ "second" ][ "third" ] = "text" ; Console.WriteLine ( t [ "first" ][ "second" ][ "third" ]);DynamicObject異なる構文を実装するためにも使用できます。
Systemを使用します。System.Collections.Genericを使用します。System.Dynamicを使用します。クラスTree : DynamicObject { private IDictionary <オブジェクト、オブジェクト> dict = new Dictionary <オブジェクト、オブジェクト> ();// t.first.second.third 構文public override bool TryGetMember ( GetMemberBinder binder , out object result ) { var key = binder . Name ;if ( dict . ContainsKey ( key ))結果= dict [ key ]; else dict [ key ] =結果= new Tree ();trueを返します; } public override bool TrySetMember ( SetMemberBinderバインダー、オブジェクト値) { dict [バインダー. Name ] =値; trueを返します; }// t["first"]["second"]["third"] の場合 構文public override bool TryGetIndex ( GetIndexBinder binder 、object [] indexes 、out object result ) { var key = indexes [ 0 ];if ( dict . ContainsKey ( key ))結果= dict [ key ]; else dict [ key ] =結果= new Tree ();trueを返します; }public override bool TrySetIndex ( SetIndexBinderバインダー、オブジェクト[]インデックス、オブジェクト値) { dict [インデックス[ 0 ]] =値; trueを返します; } }// テスト: dynamic t = new Tree (); t . first . second . third = "text" ; Console . WriteLine ( t . first . second . third );// または、動的t = new Tree (); t [ " first" ][ "second" ][ "third" ] = "text" ; Console.WriteLine ( t [ "first" ][ "second" ][ "third" ]) ;存在しない変数、または変数の位置(技術的には左辺値コンテキストと呼ばれます)を探す際に参照解除されるundefを含む変数には、自動的に適切な空項目への参照が詰め込まれます…