0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

#80 無名関数における親スコープの変数の引き継ぎ方法

Posted at

はじめに

前回の記事の中で無名関数に親スコープの変数を渡す方法として、引数で渡す他に use を使う方法をご紹介しました。(前回記事参照)
無名関数に変数を渡す方法を調べていく中で、個人的にスコープの引き継ぎに関して曖昧だった部分を整理することができたので、今回は無名関数を中心とした親スコープの引き継ぎについて、備忘録としてまとめていきたいと思います。

無名関数とは

せっかくなので、無名関数についても簡単に確認しておきましょう。


無名関数はその名の通り、関数名を指定せずに作成する関数です。
PHP関数ではcallable型のパラメータとして無名関数を渡すことができるため、引数のひとつとして使用されているものを見る機会が多いのではないでしょうか。
引数として無名関数を用いる利点としては、スコープを小さくできるというのもあります。
もちろんcallableパラメータ以外、例えば変数の値としても使用することができます。

無名関数の例

前回記事でも取り上げた array_filter() を使った例で確認してみましょう。

<?php
$heianBungaku = [
    [
        "title" => "住吉物語",
        "synopsis" => "結婚を邪魔するしつこい継母に大勝利"
    ],
    [
        "title" => "落窪物語",
        "synopsis" => "しっかり復讐するタイプのシンデレラストーリー"
    ],
    [
        "title" => "とりかへばや物語",
        "synopsis" => "父親の思い付きで始まる兄妹の入れ替わり人生"
    ],
];

// $heianbungaku から title が「落窪物語」の要素を抽出
// 引数に無名関数を使用した場合
print_r(array_filter($heianBungaku, function($value) {
    return $value["title"] == "落窪物語";
}));

// 変数に無名関数を代入した場合
$filterTitle = function($value) {
    return $value["title"] == "落窪物語";
};
print_r(array_filter($heianBungaku, $filterTitle));

// print_r -> Array ( [1] => Array ( [title] => 落窪物語 [synopsis] => しっかり復讐するタイプのシンデレラストーリー ) )
?>

use を使用した変数の引き継ぎ

上記の無名関数内の $value["title"] に一致するかどうかを親スコープで定義した変数と比較したい場合、関数に変数を渡してあげる必要があります。
しかし function($value, $xxx) のように引数を増やしても、 array_filter() で予期していた引数と実際に渡された引数の数が一致しないとして Fatal error が返されてしまいます。


無名関数で親スコープから変数を引き継ぎたいときは、use を使うことで実現することができます。

<?php
// $heianbungaku は上記と同様とするため省略

$filterTitle = "落窪物語";

// 引数に無名関数を使用した場合
print_r(array_filter($heianBungaku, function($value) use ($filterTitle) {
    return $value["title"] == $filterTitle;
}));

// print_r -> Array ( [1] => Array ( [title] => 落窪物語 [synopsis] => しっかり復讐するタイプのシンデレラストーリー ) )
?>

変数に無名関数を代入する場合でも同様です。
ちなみに、引数でも変数への代入でも、use を使って渡す変数はひとつに限りません。

<?php
// $heianbungaku は上記と同様とするため省略
$otikubo = "落窪物語"; 
$sumiyoshi = "住吉物語";

$filterTitle = function($value) use ($otikubo, $sumiyoshi) {
    return ($value["title"] == $otikubo || $value["title"] == $sumiyoshi);
};

print_r(array_filter($heianBungaku, $filterTitle));

// print_r -> 
//     Array ( 
//         [0] => Array
//             (
//                 [title] => 住吉物語
//                 [synopsis] => 結婚を邪魔するしつこい継母に大勝利
//             )
//         [1] => Array
//             (
//                 [title] => 落窪物語
//                 [synopsis] => しっかり復讐するタイプのシンデレラストーリー
//             )
//     )
?>

引き継ぐ変数の値の注意点

use を使用する際に気を付けたいのは、「変数を呼び出したタイミングではなく、定義した時の変数の値が引き継がれる」ということです。

<?php
// $heianbungaku は上記と同様とするため省略

$filterTitle = "落窪物語";

// この時点で渡される $filterTitle の値を引き継ぐ
$isMatchTitle = function($value) use ($filterTitle) {
    return $value["title"] == $filterTitle;
};

$filterTitle = "住吉物語";

print_r(array_filter($heianBungaku, $isMatchTitle));

// print_r -> Array ( [1] => Array ( [title] => 落窪物語 [synopsis] => しっかり復讐するタイプのシンデレラストーリー ) )
?>

親スコープでの変数の変更を反映させる方法としては、変数のリファレンス渡しがあります。

リファレンス渡しによる変数の引き継ぎ

リファレンス渡しとは

関数定義時にリファレンス記号「&」を変数の先頭に付けることで、親スコープで変更された内容を関数呼び出しの際に反映させることができます。

<?php
// $heianbungaku は上記と同様とするため省略

$filterTitle = "落窪物語";

// リファレンス渡しにすることで、無名関数を代入した変数$isMatchTitleの定義後に変更した、
// useで渡す変数$filterTitleの内容を反映させて呼び出せる
$isMatchTitle = function($value) use (&$filterTitle) {
    return $value["title"] == $filterTitle;
};

$filterTitle = "住吉物語";

print_r(array_filter($heianBungaku, $isMatchTitle));

// print_r -> Array ( [0] => Array ( [title] => 住吉物語 [synopsis] => 結婚を邪魔するしつこい継母に大勝利 ) )
?>

リファレンスを使用する際の注意点

リファレンスを使用することで、ふたつの変数が同じ場所を指すようになります。
ただし、以下にもあるように「リファレンスはポインタではない」という点を押さえておく必要がありそうです。

PHP において、リファレンスとは同じ変数の内容を異なった名前で コールすることを意味します。(中略)リファレンスを使ってポインタの演算をすることはできませんし、 リファレンスは実メモリのアドレスでもありません。(中略)そうではなく、リファレンスはシンボルテーブルのエイリアスです。 PHP では、変数名と変数の内容は異なっており、このため、同じ内容は異なった複数の名前を有する事が可能であることに 注意してください。(以下略)

引用:リファレンスとは?

おわりに

前回記事執筆時点では「callback関数の引数として渡せない変数には use を使う」といった認識に留めてしまっていたため、親スコープからの変数の引き継ぎ方について、今回記事でもう少し掘り下げてみました。


同時に、リファレンス渡しによる変数の引き継ぎについて調べるにあたり、リファレンスについても理解を深める良い機会になりました。
リファレンスの基本操作として本記事でも触れた「リファレンス渡し」は、関数内での変更が親スコープの変数にも反映されるため、使い所は見極める必要があるなと感じました。
正直、あえてリファレンス渡しを使用する、という状況があまり思い浮かばないので、機会があれば調べてみたいと思います。


今回はここまで。
最後まで閲覧いただきありがとうございます。


参考:

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?