これまで、変数とは「値を格納するための入れ物」と説明してきたが、少しだけ嘘が混ざっている。笑
値を格納するのは、厳密にはコンピューター上に用意されたメモリの役割である。
メモリには、それぞれの場所を表す番号(アドレス)が振られている。
しかし、コードに意味のない番号を記述するのでは、見た目もわかりにくいし、タイプミスの原因にもなる。
そこでアドレスに対して、人間が分かりやすいように名前をつけておく。
それが変数の正体である。
変数とは、値の格納先(アドレス)に対して付けられた名札と言っても良い。
メモリ | |
---|---|
アドレス | 値 |
0 | |
10 | 1 (変数$xはメモリ上のアドレス10に格納)→ $x = 1;
|
20 | |
30 | 5 (変数$yはメモリ上のアドレス30に格納)→ $y = 5;
|
40 | |
50 |
アドレス10から値を取得↓
print $x; → 1
変数=値の格納先(アドレス)に対して付けられた名札
「=」演算子で変数を変数に代入する場合は、値による代入が基本となる。
値による代入とは、メモリ上の値を別のアドレスにコピーすることをいう。
値による代入では、代入した変数と代入された変数とは別物なので、元の変数が変更されても、代入された側の変数が影響を受けることはない。
例
<?php
$x = 1;
$y = $x; //$xの値を$yにコピー
$x = 5; //$xの値を変更
print $y; //結果: 1 ($yは影響を受けない)
これに対して、参照(リファレンス)による代入とは、メモリ上のアドレスそのものを引き渡す代入のことを言います。
参照による代入では、元の変数と代入された側の変数が同じアドレスを見ている。
つまり、元の変数に対して加えた変更は代入された側の変数にも及ぶということ。
参照による代入とは、変数に「別名を与えること」と言い換えても良い。
例
<?php
$x = 1;
$y = &$x; //$xのアドレスを$yにコピー
$x = 5; //$xの値を変更
print $y; //結果: 5 ($yも影響を受ける)
代入元である変数$x
への変更が、代入先の変数$y
にも及んでいることが確認できる。
(逆に、$y
を変更した場合も$x
は影響を受ける)。
参照による代入を行うには「=」演算子の後方に参照を表す「&」演算子を指定する。
複合代入演算子である「&=」と混同しないよう注意。
ただし、オブジェクト型だけは例外で、参照による代入が規定(オブジェクトを「&」付きで代入しようとした場合、そもそもエラーとなる)。 オブジェクトを明示的にコピーするには、clone命令を利用する。
代入には、値によるものと、参照によるものがあるという点を頭に入れておく。
分割代入とは、配列/連想配列などを分解し、配下の要素を個々の変数に代入するための構文。
これには、左辺に要素の数だけ変数を列挙し、全体をブラケット([.....])でくくる。
destruct_basic.php
<?php
$data = [1, 2, 3, 4, 5];
①→ [$a, $b, $c, $d, $e] = $data;
print $a; //結果: 1
print $b; //結果: 2
print $c; //結果: 3
print $d; //結果: 4
print $e; //結果: 5
これによって、右辺のリストが個々の要素に分解されて、それぞれ対応する変数$a~$e
に代入される。
この際、左辺の要素数は右辺(配列)のそれと等しいか、少なくなければならない。
例えば以下のコードは「Undefined array key4」(インデックス番号4のキーは存在しない)のような警告となる。
$data = [1, 2, 3, 4];
[$a, $b, $c, $d, $e] = $data;
ちなみに、左辺の要素数が右辺よりも大きい場合には、不足分は無視されるだけ。
(たとえば以下↓の例では、4、5はどこにも代入されない)。
$data = [1, 2, 3, 4, 5];
[$a, $b, $c] = $data;
ブラケットによる配列の分割は、list関数でも代用できる。 例えばdestruct_basic.phpの①は以下のように表しても同じ意味。
list($a, $b, $c, $d, $e) = $data;
分割代入で途中の要素を無視するならば、ブラケット内部でスキップする数だけカンマを列挙する。
例
<?php
$data = [1, 2, 3, 4, 5];
[ , $a, , $b, $c] = $data;
print $a; //結果: 2
print $b; //結果: 4
print $c; //結果: 5
この例であれば、0、2番目の要素がスキップされる。
ただし複数の要素を連続してスキップする場合、位置関係が分かりにくくなる。
そのような場合には、ダミーの変数として「$_
」などを置く。
[$_, $a, $_, $b, $c] = $data;
構文としてスキップを意味するわけではないので、「$_
」には順に値が代入された結果、最終的な値は「3」となる。
「インデックス値 => 変数」の形式で、特定の要素だけを分割代入することも可能。
代入元の配列に対して、取り出す要素が少ない場合には、こちらの記法を利用した方がコードはシンプルになる。
例えば下記の例では、1、4番目の要素だけを取り出している↓。
<?php
$data = [1, 2, 3, 4, 5];
[1 => $a, 4 => $b] = $data;
print $a; //結果: 2
print $b; //結果: 5
この記法を利用することで、連想配列から個々の要素を取り出すこともできる。
<?php
$map = [‘title’ => ‘独学PHP’, ‘price’ => 50000];
[‘title’ => $title, ‘price’ => $price] = $map;
print $title; //結果: 独学PHP
print $price; //結果: 50000
配列/連想配列にかかわらず、指定されたキー(インデックス番号)が存在しない場合には、「Undefined array key “title”」のような警告が返される。
入れ子の配列を分割することもできる。
<?php
$data = [1, 2, [31, 32, 33]];
[$a, $b, $c] = $data; ①
print_r($a); //結果: 1
print_r($b); //結果: 2
print_r($c); //結果: Array([0] => 31 [1] => 32 [2] => 33)
[$x, $y, [$z1, $z2, $z3] = $data; ②
print $x; //結果: 1
print $y; //結果: 2
print $z1; //結果: 31
print $z2; //結果: 32
print $z3; //結果: 33
①のように、単に変数を列挙した場合には、対応する変数(ここでは$c)に入れ子の配列がそのまま代入される。
入れ子の配列も展開したいならば、②のように代入先(左辺)もブラケット([………])を入れ子にする。
同様に、入れ子の連想配列も処理できる。
例えば以下は、author-name/addressキーを取り出す例。↓
[‘author’ => [‘name’ => $name, ‘address’ => $address]] = $book;
[‘author’ => [‘name’ => $name, ‘address’ => $address]] = $book;
分割代入を利用することで、変数の値を入れ替えること(スワッピング)もできる。
もしも分割代入を利用しないのであれば、いずれかの変数をいったん別の変数に対比させる必要がある。
<?php
$x = 15;
$y = 38;
[$y, $x] = [$x, $y];
print $x; //結果: 38
print $y; //結果: 15
13: 比較演算子から