はい、どーも!
つい先日、PHPで色々とやっていたんですが、関数から複数の引数を返したいなぁ~...
という場面がありました。
そんな時の対処法を書いておきます。
目次
何がしたかった?
早速ですが...
private function hoge() {
$a = 'a';
$b = 'b';
return $a, $b;
}
public function index() {
($a,$b) = hoge();
echo($a); //a
echo($b); //b
}
イメージだとこんなことをしたかったんですね。
hogeメソッドから値を複数returnし、その値を呼び出し元で使うといったイメージです。
複数の値を返して受け取る
では、実際に書いたコードをみてみます。
配列として返す そのいち
private hoge() {
$a = 'a';
$b = 'b';
return array($a,$b); //配列として返す
// return [$a,$b]; でもOK
}
public function index() {
list($a,$b) = self::hoge();
// [$a,$b] = self::hoge(); でもOK
echo($a); //a
echo($b); //b
}
これだけです。
簡単ですね!
一旦配列にして渡してあげます。
そして、list(受け取る変数名1, 受け取る変数名2)
とすれば、複数受け取ることが可能です。
もちろん、それ以上(3つ以上の変数)でも可能です。
他にもたくさん方法はあるみたいですね...!
せっかくなので、検証してみますが、面倒だったのでLaravelでの実装をまんま貼り付けています。
基本的なところはPHPと変わりませんので...!
一応、重要そうな箇所のみコメントつけておくのでお許しくださいっ!
最終的に$techに値が入っていくイメージです。
出力は全てこうなります。
下についてるちっちゃい文字(DOCSとかその他もろもろ)はLaravelでデフォルトでついちゃうやつです。(消しとけばよかったかな)
無視してください!
重要なのは "-> <-"の中身が$resultになっていることです。
配列として返す そのに
さきほどの受け取る側のみ
class TechController extends Controller
{
public function index (Request $request)
{
$begin = '結果->';
$end = "<-";
$array = self::hoge(); // 配列$arrayとして複数の返り値を受け取る
// $arrayから一個づつ変数に代入する
$a = $array[0];
$b = $array[1];
$c = $array[2];
$result = $a . $b . $c; // abc が入る!
return view('welcometech')->with([ //表示viewに値を渡して画面に表示する
'begin' => $begin,
'result'=> $result,
'end' => $end,
]);
}
private function hoge()
{
$a = 'a';
$b = 'b';
$c = 'c';
return array($a,$b,$c); //配列として複数の値を返す
}
}
インスタンスを使う
クラスにプロパティを用意して、インスタンスのプロパティとして値を代入後、インスタンス自体を渡すという手法です。
実際に返すのは一つのインスタンスですが、そのインスタンスは3つの値をプロパティとして持っているので、結果、複数の値を返すことができるわけです。
class TechController extends Controller
{
public function index (Request $request)
{
$begin = '結果->';
$end = "<-";
$instance = self::hoge();
$result = $instance->a . $instance->b . $instance->c; // インスタンスからプロパティの値を取得
return view('welcometech')->with([
'begin' => $begin,
'result'=> $result,
'end' => $end,
]);
}
private function hoge()
{
$out = new Hoge(); // インスタンスを作成
// インスタンスのプロパティに渡したい値を渡す
$out->a = 'a';
$out->b = 'b';
$out->c = 'c';
return $out; // インスタンスを返す
}
}
class Hoge // プロパティを3つ持ったクラスを用意
{
public $a;
public $b;
public $c;
}
Hogeクラスのプロパティとして3つのプロパティを定義します。
そして、hoge関数にてインスタンスを作成(new)、作成したインスタンスの各プロパティに値を代入し、インスタンスをreturn。
メソッドから取得したインスタンスをつかって、それぞれx,y,zに代入された値を使用する。
といった流れです。
generatorを使う
これはぼくもまだあまり理解できていないのですが、おそらく、yieldを全てreturnに置き換え、関数を呼び出すと、一回めにreturnされる値(=フローを追っていって一番最初にくるyield)の値が、最初の$resultとしてforeachで取得できるといった感じだと思います。
こんな方法あったのかっ!?って目から鱗でした...w
詳しくはこちらをご覧ください。
class TechController extends Controller
{
public function index (Request $request)
{
$begin = '結果->';
$end = "<-";
$results = self::hoge(); // yieldしている関数から値を取得
$n = 0; // 結果を入れる配列のインデックス用
foreach ($results as $result) { // 取得した値を一個づつ配列に入れていく
$val[$n] = $result;
$n++;
}
$result = $val[0] . $val[1] . $val[2]; // 配列に入っている3つの値を結合
return view('welcometech')->with([
'begin' => $begin,
'result'=> $result,
'end' => $end,
]);
}
private function hoge()
{
// 渡したい引数をyieldで指定する
yield "a";
yield "b";
yield "c";
}
}
参照渡しで値を返さずに値を変える
参照渡しをしてあげれば実際の返り値はなくても複数の値を返すような挙動(複数の値を別メソッドで書き換えて、呼び出し元メソッドでつかう)をすることができますね。
それを応用しているものです。
class TechController extends Controller
{
public function index (Request $request)
{
$begin = '結果->';
$end = "<-";
// 適当な変数を定義しておく(PHPしなくても大丈夫だった気がするけど分かりやすいように)
$a = "";
$b = "";
$c = "";
self::hoge($a,$b,$c); // 最後に結果をいれたい変数を参照渡しする
$result = $a . $b . $c; // メソッドが呼ばれて値が代入されている!!!
return view('welcometech')->with([
'begin' => $begin,
'result'=> $result,
'end' => $end,
]);
}
private function hoge(&$a, &$b, &$c)
{
// 参照渡しされたものに代入をするので、実質indexメソッドの変数$a, $b, $cに代入している
$a = "a";
$b = "b";
$c = "c";
}
}
連想配列とextractを使う
最後に、連想配列として渡してあげて、受け取る側でextractを使うことで変数として使えるようにしてあげるパターンです。
extractを使うことによって、連想配列のkeyを変数名、valueを値とした変数が使えるので、['hoge' => 'huga']
という連想配列を返し、それに対してextractをすると、huga
という文字列の入った$hoge
変数が使えるようになるわけです。
class TechController extends Controller
{
public function index (Request $request)
{
$begin = '結果->';
$end = "<-";
$result = self::hoge(); // 連想配列が渡される
extract($result); // extractによって、keyが変数名、valueが値として変数を使えるように
$result = $a . $b . $c; // インスタンスからプロパティの値を取得
return view('welcometech')->with([
'begin' => $begin,
'result'=> $result,
'end' => $end,
]);
}
private function hoge()
{
return [ 'a' => 'a' , 'b' => 'b', 'c' => 'c'];
}
}
いろんな方法をみてきたけど...?
正直な気持ちをいうと、"一つの関数が複数の値を返す=一つの関数が複数の責務を持っている"ので、そもそもそんな設計間違ってんぜ!
っていう気も...しなくもないです。
如何しようも無い場合もあるかもしれないので、そんな時は是非上記手法から色々と試してみてください。
あと、正直yieldとかはあんまり使われていないのでは...?(少なくとも複数の値を返すため!という目的での導入は怪しいかも...)
もうちょっと複雑で、yieldの活躍する場とかがありそうだったので、そういう場合に使ったほうが良さそうだなと感じました。
無難なのは配列で渡す、連想配列で渡す、参照渡し辺りなのかなぁ・・・?
でもいろんな実装方法があるんだっ!って知っているだけでも違うので、是非みなさんもお手元で確認してみてくださいね!
それでわ!