10週間ウェブ開発講座

裏メニュー第一週

ユーザー定義関数

PHPでユーザー定義関数を扱う方法について解説します。

基本的なユーザー定義関数

関数は次のような構文で定義されます。

  1. <?php
  2. function foo($arg_1, $arg_2, /* ..., */ $arg_n)
  3. {
  4.   echo "関数の例\n";
  5.   return $retval;
  6. }
  7. ?>

この例を元に関数の構成要素を示します。

  • 関数名: foo
  • 引数(または仮引数): $arg_1, $arg_2, ..., $arg_n (引数は0個以上存在)
  • 本体: 3行目から6行目 (すなわち{}でくくられる部分)
  • 返値(または戻値): $retval (return の後ろに来る値)

それぞれの構成要素について述べる前に、単純な関数を作成し、実行してみます。

  1. <?php
  2. function multiple($x, $y)
  3. {
  4.   $ret = $x * $y;
  5.   return $ret;
  6. }
  7.  
  8. echo multiple(3, 4); // => 12
  9. ?>

このプログラムは最終的に "12" を出力します。

8行目で関数multipleを引数(または実引数)に3と4を与えて呼び出しています。
この時、3、4をそれぞれ第一引数、第二引数と呼びます。

multipleが呼び出されると、multipleの本体に書かれている処理が実行されます。
multipleの本体では、渡ってきた引数3、4で初期化された$xと$yを使用することができます。
4行目で$xと$yを掛けた値を$retに格納し、それを5行目で返値にしています。
返値とは「関数の実行結果として返る値」です。
したがって、multiple(3, 4)を実行すると12が返るのです。

デフォルト引数値を指定したユーザー定義関数

ユーザー定義関数にデフォルト引数値を指定することができます。
半径と円周率を引数として渡すと円の面積*1を返す関数を定義してみましょう。

  1. <?php
  2. function circle_area($r, $pi = 3)
  3. {
  4.   $area = $r * $r * $pi;
  5.   return $area;
  6. }
  7.  
  8. echo circle_area(5) . "\n"; // => 75
  9. echo circle_area(5, 3.14) . "\n"; // => 78.5
  10. ?>

8行目のcircle_area呼び出しでは第二引数を指定していません。
この時、circle_area内で$piは3で初期化されます。
仮引数のデフォルト引数値が3だからです。

9行目のcircle_area呼び出しでは第二引数が3.14と指定されています。
この時、circle_area 内で$piは3.14で初期化されます。
仮引数のデフォルト引数値は無視されます。 デフォルト引数値は引数が指定されないときのみ有効です。

デフォルト値を有する引数はデフォルト値がない引数の右側に全て存在する必要があります。
つまり、次のように定義した関数は期待通り動作しません。

  1. <?php
  2. function sample($a = 'foo', $b)
  3. {
  4.   // do something
  5. }
  6. ?>

これは、次のように書き直されるべきです。

  1. <?php
  2. function sample($b, $a = 'foo')
  3. {
  4.   // do something
  5. }
  6. ?>

値を返さないユーザー定義関数

関数が値を返さないこともあります。
先ほどのプログラムと同様のことを、値を返さない関数を使用して実現すると次のようになります。

  1. <?php
  2. function circle_area($r, $pi = 3)
  3. {
  4.   $area = $r * $r * $pi;
  5.   echo "{$area}\n";
  6. }
  7.  
  8. circle_area(5);
  9. circle_area(5, 3.14);
  10. ?>

引数を参照渡しで受け取るユーザー定義関数

引数として与えられる変数の値を交換する関数を考えてみましょう。
次のように関数swapを作成してみます。
これは上手く機能するでしょうか?

  1. <?php
  2. function swap($a, $b)
  3. {
  4.   $tmp = $a;
  5.   $a = $b;
  6.   $b = $tmp;
  7. }
  8.  
  9. $a = 1;
  10. $b = 2;
  11.  
  12. swap($a, $b);
  13.  
  14. echo "{$a}\n"; // => 1
  15. echo "{$b}\n"; // => 2
  16. ?>

値の交換が出来ていないようです。
関数呼び出しの時に仮引数が受け取るのは実引数のであり、実引数そのものではないことが問題です。
関数内での仮引数の値の変更を実引数に反映させる場合は関数の引数を参照渡しにする必要があります。
関数定義においてアンパサンド(&)を引数名の前に付加することで、その引数が参照渡しであると指定できます。

以上のことを踏まえ、swap関数を書き替えます。

  1. <?php
  2. function swap(&$a, &$b)
  3. {
  4.   $tmp = $a;
  5.   $a = $b;
  6.   $b = $tmp;
  7. }
  8.  
  9. $a = 1;
  10. $b = 2;
  11.  
  12. swap($a, $b);
  13.  
  14. echo "{$a}\n"; // => 2
  15. echo "{$b}\n"; // => 1
  16. ?>

今度は値の交換に成功しましたね。

再帰的なユーザー定義関数

自分自身を使って自分を定義する関数があります。
これを再帰的な関数と呼びます。

例えば階乗を計算するfactという関数について考えましょう。
自然数nの階乗n!とは、1からnまでの自然数の総乗です。
つまり

n! = n * (n - 1) * (n - 2) * ... * 3 * 2 * 1
5! = 5 * 4 * 3 * 2 * 1

という数です。

ではfactを定義しましょう。

  1. <?php
  2. function fact($n)
  3. {
  4.   if (1 === $n) {
  5.     return 1;
  6.   } else {
  7.     return $n * fact($n - 1);
  8.   }
  9. }
  10.  
  11. echo fact(1) . "\n"; // => 1
  12. echo fact(2) . "\n"; // => 2
  13. echo fact(3) . "\n"; // => 6
  14. echo fact(4) . "\n"; // => 24
  15. echo fact(5) . "\n"; // => 120
  16. ?>

これと同様の値を返す関数をforなどのループを使用して記述できます。

  1. <?php
  2. function fact($n)
  3. {
  4.   $ret = 1;
  5.   for ($n; $n > 0; $n--) {
  6.     $ret = $ret * $n;
  7.   }
  8.   return $ret;
  9. }
  10.  
  11. echo fact(1) . "\n"; // => 1
  12. echo fact(2) . "\n"; // => 2
  13. echo fact(3) . "\n"; // => 6
  14. echo fact(4) . "\n"; // => 24
  15. echo fact(5) . "\n"; // => 120
  16. ?>

宿題

  • 関数factについて、「再帰バージョン」と「ループバージョン」とどちらが良いか説明してください
  • htmlspecialcharsと同程度の機能を持つ関数my_htmlspecialcharsを作成してください
    • my_htmlspecialcharsは第一引数に文字列を受け取ります
    • my_htmlspecialcharsは第二~第四引数にオプションを受け取るかもしれません
    • my_htmlspecialcharsは特殊文字をエスケープした文字列を返値にします
    • my_htmlspecialcharsはhtmlspecialcharsを関数本体に含みません
  • N番目のフィボナッチ数を返す関数fibonacciを作成してください
    • バージョン1: 再帰的な関数
    • バージョン2: ループを含む関数

フィボナッチ数は次のような数です。

0番目: 0
1番目: 1
2番目: 1
3番目: 2
4番目: 3
5番目: 5
6番目: 8
7番目: 13
8番目: 21
9番目: 34
10番目: 55

詳しくは参考資料を確認してください。

  • (オプション:難しい) マインスイーパのパネル開けのアルゴリズムを考案してください

参考資料


*1 円の面積は「半径*半径*π」

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2009-09-02 (水) 15:43:56 (368d)