PHP/配列
PHPにおいて、配列は非常に重要なデータ構造の一つです。配列を使うことで、多くのデータを効率的かつ簡単に扱うことができます。PHPにおいては、狭義の配列と連想配列の2種類の配列があります。狭義の配列は、数値添字の配列を表し、連想配列は、文字列添字の配列を表します。この章では、狭義の配列と連想配列の基本的な使い方を学び、配列を使ったプログラムを作成するための基礎を身につけます。
配列
[編集]配列( array )は、数値や文字列など任意の型の値を順番を持って保持するデータ型です。
配列リテラル
[編集]配列リテラル( array literal )は、要素を ,
(カンマ)で区切り全体を [ ]
(角括弧) で囲んで表します。
最後の要素のあとの,
はあってもなくても構いません。
- 例
<?php $a = [1,3.14,"ABC",]; $b = array(1,3.14,"ABC"); echo 'gettype($a) --> ', gettype($a), PHP_EOL; echo 'get_debug_type($a) --> ', get_debug_type($a), PHP_EOL; echo PHP_EOL; var_dump($a == $b); ?>
- 実行結果
gettype($a) --> array get_debug_type($a) --> array bool(true)
- 配列は、スカラーと同じく $a や $b のような変数に束縛できます。
- $ が @ になったりはしません。
[ … ]
はarray( … )
の短縮表記です。- gettype() と get_debug_type() は、オブジェクトの型を文字列で返す変数で、配列に関しては同じ結果を array を返します。
- 例
<?php $wdays = ["月", "火", "水", "木", "金", "土", "日"]; ?> <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"><html> <title>配列の例</title> </head> <body> <?php for ($i = 0, $len = count($wdays); $i < $len; $i++): ?> <p><?= "[$i]: $wdays[$i]" ?></p> <?php endfor; ?> <pre><?= var_export($wdays,true) ?></pre> </body> </html>
- 実行結果(HTML)
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"><html> <title>配列の例</title> </head> <body> <p>[0]: 月</p> <p>[1]: 火</p> <p>[2]: 水</p> <p>[3]: 木</p> <p>[4]: 金</p> <p>[5]: 土</p> <p>[6]: 日</p> <pre>array ( 0 => '月', 1 => '火', 2 => '水', 3 => '木', 4 => '金', 5 => '土', 6 => '日', )</pre> </body> </html>
- レンダリング例
[0]: 月
[1]: 火
[2]: 水
[3]: 木
[4]: 金
[5]: 土
[6]: 日
array ( 0 => '月', 1 => '火', 2 => '水', 3 => '木', 4 => '金', 5 => '土', 6 => '日', )
var_export()
関数は、変数の値を PHPのリテラルの形式で文字列化します、- なぜか => を使った形式ですが、これた配列が連想配列の特殊なケースだからで、詳しくは連想配列で解説します。
- 組込み関数 count は、配列の要素個数を返します。
$ary[] = 42 は array_push($ary, 42)
[編集]$ary[] = 42 は array_push($ary, 42) と同じ意味で、配列変数 $ary の末尾に 42 が追加されます。
- 例
<?php $ary[] = 42; echo var_export($ary,1), PHP_EOL; array_push($ary, 42); echo var_export($ary,1), PHP_EOL; ?>
- 実行結果
array ( 0 => 42, ) array ( 0 => 42, 1 => 42, )
- ただし、配列がない時(からの配列があるのではなく $ary そのものがない時)は、
array_push($ary, 42);
はエラーになり、$ary[] = 42;
は$ary[0] = 42;
と解されます。
疎な配列
[編集]数学で、疎行列(スパース行列)と言うと殆どの要素が 0 の行列ですが、PHPの疎な配列は値のない要素がある配列です。
- 例
<?php $ary = [2,3,5]; echo var_export($ary,1), PHP_EOL; $ary[100] = 123; echo var_export($ary,1), PHP_EOL; echo 'count($ary) --> ', count($ary), PHP_EOL; foreach ($ary as $el) { echo "$el "; } ?>
- 実行結果
array ( 0 => 2, 1 => 3, 2 => 5, ) array ( 0 => 2, 1 => 3, 2 => 5, 100 => 123, ) count($ary) --> 4 2 3 5 123
- 添字3から添字99までは対応する要素がないという状態です。
- これでは、foreachは機能しますが、for(;;)は機能しません。
- 配列の順位を間違えると、疎行列を作ってしまうので配列の末尾に追加したい場合は
$配列変数[] = 値
の形式にするのが良いでしょう。
負のインデックス
[編集]PHPの配列のインデックスに負の整数を使うのは合法です。
- 例
<?php $ary[-12] = 12; echo var_export($ary,1), PHP_EOL; $ary[] = 123; $ary[] = 666; echo var_export($ary,1), PHP_EOL; echo 'count($ary) --> ', count($ary), PHP_EOL; foreach ($ary as $el) { echo "$el "; } ?>
- 実行結果
array ( -12 => 12, ) array ( -12 => 12, -11 => 123, -10 => 666, ) count($ary) --> 3 12 123 666
- 負の順位の値を代入すると、負の順位を持つ要素ができます。
- この状態で、
$配列変数[] = 値
の形式でプッシュすると、最大の順位の次の順位(1つ大きい順位)に要素が作られます。 - 疎配列と合わせて奇異な挙動ですが、一貫性はあります。
多次元配列
[編集]配列の中に配列を入れることができ、これは二次元配列になります。
配列の中に二次元配列を入れることができ、これは三次元配列になります。
配列の中に三次元配列を入れることができ、これは四次元配列になります。
︙
- 例
<?php $ary1 = [0,1]; $ary2 = [[1,0],[0,1]]; $ary3 = [[[1,0],[0,1]],[[0,1],[1,0]]]; var_dump($ary1); echo PHP_EOL; var_dump($ary2); echo PHP_EOL; var_dump($ary3); echo PHP_EOL; ?>
- 実行結果
array(2) { [0]=> int(0) [1]=> int(1) } array(2) { [0]=> array(2) { [0]=> int(1) [1]=> int(0) } [1]=> array(2) { [0]=> int(0) [1]=> int(1) } } array(2) { [0]=> array(2) { [0]=> array(2) { [0]=> int(1) [1]=> int(0) } [1]=> array(2) { [0]=> int(0) [1]=> int(1) } } [1]=> array(2) { [0]=> array(2) { [0]=> int(0) [1]=> int(1) } [1]=> array(2) { [0]=> int(1) [1]=> int(0) } } }
連想配列
[編集]連想配列( associative array )は、キー(整数や文字列など任意のスカラー型値)と値(任意の型)をペアとした集合を保持するデータ型です。 値は重複して構いませんが、キーは1つの連想配列の中ではユニークでなければいけません。
PHPでは、配列と連想配列の区別がありません。配列は整数をキーとする連想配列です。
連想配列リテラル
[編集]連想配列リテラル( associative array literal )は、キー => 値
ペアを ,
(カンマ)で区切り全体を [ ]
(角括弧) で囲んで表します。
最後の要素のあとの,
はあってもなくても構いません。
- 例
<?php $hash = [ "pi" => 3.14159265359, "e" => 2.71828182846, 3.14159265359 => "pi", 2.71828182846 => "e", ]; echo 'gettype($hash) --> ', gettype($hash), PHP_EOL; echo 'get_debug_type($hash) --> ', get_debug_type($hash), PHP_EOL; echo "\$hash('pi') => ", $hash['pi'], PHP_EOL; echo PHP_EOL; foreach ($hash as $key => $value) { echo "\$key($key) => \$value($value)", PHP_EOL; } var_export($hash); ?>
- 実行結果
gettype($hash) --> array get_debug_type($hash) --> array $hash('pi') => 3.14159265359 $key(pi) => $value(3.14159265359) $key(e) => $value(2.71828182846) $key(3) => $value(pi) $key(2) => $value(e) array ( 'pi' => 3.14159265359, 'e' => 2.71828182846, 3 => 'pi', 2 => 'e', )
- 配列は、スカラーと同じく $hash のような変数に束縛できます。
- $ が % になったりはしません。
[ … ]
はarray( … )
の短縮表記です。- gettype() と get_debug_type() は、オブジェクトの型を文字列で返す変数で、連想配列に関しては同じ結果を array を返します。
- キーに数値を与えると、整数に丸められます(エラーにはなりません)。
- 例
<?php $wdaysE2J = [ "Mon" => "月", "Tue" => "火", "Wed" => "水", "Thu" => "木", "Fri" => "金", "Sat" => "土", "Sun" => "日", ]; ?> <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"><html> <title>連想配列の例</title> </head> <body> <?php foreach ($wdaysE2J as $en => $ja): ?> <p><?= "$en => $ja" ?></p> <?php endforeach; ?> <pre><?= var_export($wdaysE2J, true) ?></pre> </body> </html>
- 実行結果(HTML)
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"><html> <title>連想配列の例</title> </head> <body> <p>Mon => 月</p> <p>Tue => 火</p> <p>Wed => 水</p> <p>Thu => 木</p> <p>Fri => 金</p> <p>Sat => 土</p> <p>Sun => 日</p> <pre>array ( 'Mon' => '月', 'Tue' => '火', 'Wed' => '水', 'Thu' => '木', 'Fri' => '金', 'Sat' => '土', 'Sun' => '日', )</pre> </body> </html>
- レンダリング例
Mon => 月
Tue => 火
Wed => 水
Thu => 木
Fri => 金
Sat => 土
Sun => 日
array ( 'Mon' => '月', 'Tue' => '火', 'Wed' => '水', 'Thu' => '木', 'Fri' => '金', 'Sat' => '土', 'Sun' => '日', )
var_export()
関数は、変数の値を PHPのリテラルの形式で文字列化します、
疎な配列と負のインデックスの意味と意義
[編集]配列の項目で疎な配列と負のインデックスについて扱いましたが、配列がキーを整数に限定した特殊なケースであることがわかると、一見奇異な挙動が、連想配列の基本機能からくるものだったとわかります。このように連想配列のインフラを使って配列を実装している処理系に JavaScript があります。
この実装上の選択の理由はいくつかありますが
- 動的言語は、実行時に配列要素が増えることがある(ほぼ確実に)
- PythonのようにListとTupleに分ける方法もありますが、PythonのListは名前に反してHash型の実装です。
- 添字によらず参照時間を一定にしたい
- 線形リストで実装するとインデックスが大きいと応答が遅くなり、アルゴリズムによっては理論値とかけ離れた性能劣化となる。
- AVL木のような方法で平滑化されたアクセス時間を目指すと、メモリーフットプリントの増加が無視できない。
⇒ インデックス(=整数値)をハッシュキーとしハッシュテーブル(連想配列)のインフラを使い実装する