本稿の目標
- 関数というものがわかる
- 関数の使い方がわかる
はじめに
制御構文と変数で、結構多くのことができるようになったのではないだろうか。
プログラムとは極論を言えば、値を条件判断して適切な出力を求めることに集約されるので、値を取り出す方法、それを判断する方法、出力する方法が確立されれば、理屈上何でもできるようになる。
ただ自分で実装するよりも早くて、簡単で、確実な方法があればそちらを利用したいだろう。
そのための仕組みの一つが関数である。
関数とは
関数とは複数の処理をひとまとめにして、入力された値に対して一定の結果を得るためのルール定義である。
一般的に関数というと数学の関数だろうか。
y=f(x)
という数式は「xを与えるとyが決まる」という表現である。
コンピュータの文脈で言う関数もその意味合いで使われる。
例えばみんな大好きExcel関数も
=SUM(A1:A3)
と書くことで、A1〜A3の値を与えることで合計という一意の値が得られることを意味している。
PHPでも考え方は同様なので、身構えずに読み進めていってほしい。
関数を使うとき
今のサンプルを覗いてみよう。
<?php $job = $_REQUEST['job'] ?>
<html>
<head>
<title>選択された職業</title>
</head>
<body>
<h1>あなたの職業</h1>
<p>
選んだ職業は<?php echo $job ?>です。
</p>
<p>
<strong>性格</strong>
<?php if('warrier' == $job){ ?>
何事にも基本を大事にするタイプです。
<?php } else { ?>
まだ性格診断ができていません。
<?php } ?>
</p>
</body>
</html>
この性格診断を増やしていくときにはどうするだろうか。
例えば、こんな形だろうか。
<?php $job = $_REQUEST['job'] ?>
<html>
<head>
<title>選択された職業</title>
</head>
<body>
<h1>あなたの職業</h1>
<p>
選んだ職業は<?php echo $job ?>です。
</p>
<p>
<strong>性格</strong>
<?php if('warrier' == $job){ ?>
何事にも基本を大事にするタイプです。
<?php } elseif('mage' == $job) { ?>
戦略を重んじるタイプです。
<?php } else { ?>
まだ性格診断ができていません。
<?php } ?>
</p>
</body>
</html>
そんなことはしたくない!
このままだと職業の数だけelseifが増えていくことになる。
ちょっと進んでswitchを使ってもいいが、どちらにしても診断内容が増えるたびに変更をしていくことに疑問を覚えてみよう。
プログラマの三大美徳の怠惰というものだ。
関数を使ってみよう
やりたいこと
将来的に判断すべきことが増えていくことが見えているので、そのたびにコードの修正をしないですむようにしたい。
方針
外部ファイルを使って、職業と診断結果の組み合わせを増やしていけるようにする。
ついでに英語表記の職業も日本語にしてみよう。
このときに、
- 外部ファイルを読み込む
- ファイルの内容をプログラム中で利用する
ことはきっと多くの人が必要としている処理なので、関数がないかを探してみよう。
外部ファイルの形
使い勝手が良さそうなのでJSONにしてみよう。
{
"warrier": { "name": "戦士", "type": "何事にも基本を大事にするタイプです。"},
"mage": {"name": "魔法使い", "type": "戦略を重んじるタイプです。"}
}
サンプルコード
<?php
$job = $_REQUEST['job'];
$jobs_json = file_get_contents('jobs.json');
$jobs = json_decode($jobs_json, true);
$selected_job = $jobs[$job];
$job_name = $selected_job['name'];
$job_type = $selected_job['type'];
?>
<html>
<head>
<title>選択された職業</title>
</head>
<body>
<h1>あなたの職業</h1>
<p>
選んだ職業は<?php echo $job_name ?>です。
</p>
<p>
<strong>性格</strong>
<?php echo $job_type ?>
</p>
</body>
</html>
一気にプログラムっぽくなってきたように見えるが、解説をしていくので安心してほしい。
何がうれしくなったか
やりたいことで書いたように、職業の追加に対してプログラムの修正が一切不要となった。
まずはhttp://localhost:8000/selected_job.php?job=warrierやhttp://localhost:8000/selected_job.php?job=mageでアクセスしてみよう。
該当職業の情報が出てくるだろう。
新しい職業を追加するときにはJSONファイルを編集すれば良い。
{
"warrier": { "name": "戦士", "type": "何事にも基本を大事にするタイプです。"},
"mage": {"name": "魔法使い", "type": "戦略を重んじるタイプです。"},
"thief": {"name": "盗賊", "type": "効率を重視するタイプです。"}
}
http://localhost:8000/selected_job.php?job=thiefで確かめてみよう。
すばらしい!
JSONファイルを読み込み、要求された職業の情報を取得して表示するようになっていることがわかる。
解説
先頭に(今までに比べると)大量のPHPコードが書かれているので、そこを解説していこう。
$jobs_json = file_get_contents('jobs.json');
$jobs = json_decode($jobs_json, true);
いきなり2つの関数呼び出しが並んでいる。
これは、「JSONファイルを読み込んでうまく使いたい」という要望に直接応えてくれる関数がないので、二つの関数を順に処理することで、期待する結果を得るための手順だ。
func(arg1,arg2...)
という形が関数呼び出しの合図だ。
()
内のarg1など、関数に引き渡す値を**引数(ひきすう)**と呼ぶ。
-
file_get_contents
- 引数として渡された文字列をファイル名とみなして、そのファイルの内容を取得して文字列として返してくれる関数である。
-
json_decode
- 引数で渡された文字列をJSON規則を守っているものとみなし、各要素へのアクセス方法をもったデータに変換してくれる。
なんだって!?
関数は与えられた値を計算するものではないの?
合計を出したりとか。
上で書いた言葉を再度見てみよう
関数とは複数の処理をひとまとめにして、入力された値に対して一定の結果を得るためのルール定義である。
数値計算に限った話ではない。
入力された「文字列」に対して、一定の結果(ファイルの内容やJSONの各要素にアクセスしやすいデータ)を得ることも、立派な関数なのである1。
この感覚が初心者にとって馴染みのないものかもしれない。
ともかく、2つの関数を経由することで、ファイル名からそのファイルに書いてある内容を利用できるデータに「変換」しているのだ。
そしてそれらは一時的に値を保管するために都度変数として名前をつけている。
その方が使いやすいからと思っていただければいい。
今、$jobs
の中には、JSONファイルで定義した各職業の情報がすべて入っている。
しかし、ここでほしいのはユーザが選択した職業なので
$selected_job = $jobs[$job]; // $jobsは各職業の定義、$jobはパラメータで渡された'warrier'などが入っている
として、必要な職業の情報だけに絞ってみた。
このような関係性であることをイメージしてほしい。
あとは前稿の復習で、変数を使って必要な情報へのアクセスをしやすくしている。
$job_name = $selected_job['name']; // $selected_jobで指し示されたデータのname項目を見るときにはこのように書けば良い
$job_type = $selected_job['type'];
上の図でどの青いエリアにもnameやtypeがあるので、入力された値によって$selected_job
が変われば、必要な情報が手に入れられることになる。
そして、外部ファイルで各職業の情報を定義するように変更したため、今まで条件分岐で表示を切り替えていた性格診断が、取得した情報を表示するだけで良くなっている。
<strong>性格</strong>
<?php echo $job_type ?>
という条件判断から
という方針に変えたことによるコードの変更である。
関数を使ったことは直接関係ない。
しかし、この方針を実現するために便利な関数が必要だったことは間違いないので、関数利用による恩恵とはいえる。
結局関数とは何なのか
多くのプログラム開発者が必要としそうな汎用的な処理を簡単に実現することを助けるためのものである。
今回のケースでは、「ファイルを読み込みたい」「JSONのデータを簡単に扱いたい」という欲求を、うまく実現している関数が見つかったので利用できたわけである。
関数の知識
何も準備をせずに使える基本的な関数のことを「標準関数」とか「組み込み関数」と呼ぶ。
シリーズの中でライブラリなどについて言及する機会が訪れるが、そういった特別なファイルを用意しなくても使える関数のことである。
言語によって備えている組み込み関数の量は違う。
中でもPHPは組み込み関数がとりわけ多い言語であると言われている。
例えば、今回のケースで利用したJSONデータを取得するjson_decode
のような関数を標準で備えているのはPHPとJavaScriptぐらいではないだろうか2。
今回、関数に初めて触れた初心者の気持ちを想像しながら、大量の関数と向き合うアドバイスを記してみよう。
関数を覚えなければならないか
覚えた方が効率がいい。
しかし、覚えなくてもなんとかなる。
実際、筆者は今回使った2つの関数を覚えていない3が、簡単にググって見つけて利用している。
便利な世の中になったものだ。
ググっていいの?
むしろググろう。
ポケットリファレンスを手にして開発する時代は今や20年くらい前に終わっている。
人の記憶なんて当てにはならないのだから、積極的にググってその言語に精通していこう。
ただ、自分が利用している言語の公式リファレンスくらいは把握しておこう。
PHPであればこちらだ。
ググり方は?
言語名、対象のデータ、処理したい方法を組み合わせると、だいたい見つかる。
今回筆者がググったときは「php ファイル 読み込み」(しかもサジェスト)であったり、「php json パース」だったりする。
ただし、最初にも書いたが、コピペで済ましていては理解が進まないので、動かすことよりもたどり着いた記事の内容を理解できるように努めよう。
ググって見つかるもの
自分で作らなければならないか、標準機能が使えるのかというのは結構経験則に頼ることになり、何を検索すべきかの判断に迷うのが初心者だろう。
例えば、入力された社員番号から社員名を取得したい、という時には何をググればいいのだろうか4。
検索で得られるものは、
- 汎用的な関数
- サンプルコード
- 一般的に誰かが解決しているだろう問題に対するサンプルコード
- 例えばphpからgoogle mapに任意のピンを立てる方法とか
- ランダムでn桁の文字列を作る方法とか
- どこかで「こんな機能が実現されていたな」と思ったらそれは解決済みであることが多い
- しかし、あくまでサンプルなので自分のコードとイコールではないため、きちんと理解してアレンジできるようにする
なので、今あなたが直面している問題そのものを解決するものではない。
何度も繰り返しになるが、コードの意味を常に理解して進めていくことが大事だ。
とはいえ、見つかりそうな経験則を言語化することもある程度役に立つのではないかと思うので、検索時の参考にしてほしい。
だいたい標準でありそうな機能・関数
- 文字列をごにょごにょしたい
- 「,」で分割してn個めがほしいときとか
- 文字列の置換をする
- 繰り返す
- 特定の文字があるか判定
- etc
- 検索ワード「PHP 文字列 (分割、結合、置換、繰り返し、フィルタ)」など
- 基本的な数字の扱い
- 切り上げ・切り捨て・四捨五入
- 絶対値
- n乗、n乗根
- etc
- 検索ワード「PHP 数値 (切り上げ、絶対値、n乗)」など
- 日付の扱い
- n日後・前
- 二つの日付の差は何分?
- etc
- 検索ワード「PHP 日付 (加算・差・秒のみ)」など
- 配列の扱い
- 特定のものだけ抽出
- 一括でなにか処理させたい
- キーだけ、値だけ取り出したい
- 結合して文字列にしたい
- 検索ワード「PHP 配列 (抽出・一括・キーのみ・結合)」など
- OSとの連携
- ファイルを操作する
- 入力を促し待つ
- 画面のどこかに出力する
- 他アプリへつなげる
- 検索ワード「PHP (ファイル・入力・画面) (読み込み・書き出し・送信)」など
- etc
拡張機能をつかうとできそうなこと(PHPでは標準でできたりすることに驚く)
検索ワードは考えてみよう6。
- 時代はやっぱり暗号
- 暗号化
- 復号
- ランダム文字列の生成
- 一般的なデータ形式の取り扱い
- CSV
- JSON
- mp3
- html
- データベース
- ネットとつながる
- 外部サイトの情報を取得
- 外部サイトにデータを送る
- メールを送る
- etc
サンプルコードが見つかりそうなこと
- 有名サービスとの連携
- AWS
- GCP
- slack
- LINE
- etc
- ちょっと複雑だけど一般的に需要がありそうな処理
- ブラウザにドロップした画像ファイルを表示する
- 画像ファイルの圧縮方法
- 指定された時間に動くプログラム
- IPアドレスからおおよその地域を取得する
- etc
- エラーがあったときの回避方法
- エラー文をそのままググっても見つかる
- 有名な問題の解答
- FizzBuzz
- Project Euler
- etc
まとめ
関数とは
- 汎用的な処理をひとまとめにして使いやすくしたもの
- 入力に対して期待する結果を返してくれるもの
- 自分の抱えている課題をドンピシャで解決する関数を期待してはいけないので、組み合わせて使うことを前提にしよう
関数の使い方
- 呼び出したら結果が値として扱われるので、そのまま変数に放り込めば結果を使える
- 関数を呼び出すときも結果も、お作法(どんなデータに反応できるのか)があるのできちんと理解しよう
関数についての知識
- ググれ
- 多くの言語がカバーしている関数は似たり寄ったりなので、ググるにも経験値がものを言う
- 最初のうちはググり方を知り合いのエンジニアに相談するのもよい
- この記事も参考にしてググり方を学んでみよう
- 初心者の壁を乗り越えるために一度はリファレンスに目を通すこと
落穂ひろい
ここまでの中で、サンプルコードを中心に使い方を学んできたが、具体的にphpの仕様を伝えていないので、基本的なことをまとめておこう。
今すぐ理解して覚える必要はないが、徐々にphpの文化に慣れていくときの足がかりとしていただきたい。
文法とは
プログラミング言語はphpに限らず文法を持っている。
これは単なる文字列の集合であるプログラムソースを理解し、コンピュータ上で実行するためのお作法である。
例えばrubyで実行可能な記述
print "Hello, world!"
がphpで実行できないのは単純に「phpの文法通りに解釈できない」からである。
プログラムがどのように動くのかを知ると、文法の必要性を理解する助けになるかもしれないので、簡単に説明を加える。
書かれたプログラムコードは対象の言語によって、コンピュータの理解可能な状態に翻訳する。これをコンパイルと呼ぶ。
コンパイルは決められた仕様のみを対象とするため、人間にとって自明な命令「ブラウザを最大化する」という言葉を解釈しない。
つまり、我々がコンピュータで何かを作業するには、プログラミング言語の仕様に従ってコードを記述することが最も「わかりやすい」やり方となる。(直接バイナリを作るよりは)
プログラミング「言語」が翻訳を行う仕様を、「文法」と呼んでいる。
文法の種類
言語によって項目の多寡はあるが、だいたいこのような分類で文法は作られている。
- 表記形式
- シンタックス(syntax)
- 制御構造を作るときの{}や関数呼び出しの()、文の終わりを示す;など、コードの記述方法に関わる形式
- リテラル(literal)
- 文字列を表す''や""、小数点が.である、などのデータを示す表記方法
- シンタックス(syntax)
- 実行順序
- 上から順番に実行されるのか、行頭の番号で実行順を決める(BASIC)かなど
- プログラムが読み込まれたときに最初に実行する場所はどこか(エントリポイント)
- 関数の中に他の関数があった場合、どのように実行されるのか(コールスタック)
- 型システム
- コンピュータはバイナリ(0/1)しか理解しないため、文字列や数値、配列(今後説明予定)などの、人にとって馴染みやすいデータの持ち方を定めたもの
- また、その内部的な取り扱い方、制限など
PHPの文法(手抜き解説)
主なシンタックス
- PHPのコードは必ず
<?php
から始まる - 変数は
$
で始まるひとまとまりの語句(スペースや().,{}'"
などの記号で区切られた単位)で示される - 関数の呼び出しは
()
を伴う- 引数なしの場合でも
()
を省略することはできない
- 引数なしの場合でも
- 文末は
;
を必要とする - 1文で1処理のみが実行できる
-
echo '1st' echo '2nd'
のような書き方はできない
-
- 文中に関数があった場合は関数を呼び出した結果を値として利用する
-
(){}'"
などは対で用いなければならない-
if(関数{処理)}
のように対の入れ替わりは解釈できない
-
- 制御構文は処理内容を
{}
で区切る -
//
に続く行末まで、あるいは/*
〜*/
の間、はコメントとして扱われ、何も実行しない - 演算子は括弧を伴わない
- 演算子とは複数の値を合成するときの命令群
- 主要な算術演算子、文字列を結合する演算子など、基本的な処理を簡易化するために用意されている
-
1 + 1
,3 - 1.5
,'文字列を' . '結合する'
など
主なリテラル
-
'
または"
で囲まれた値は文字列として扱う - 0〜9、-で始まる語句は数値として扱う
- 小数点は.で表す(文化的に,を小数点とする文化圏もあるが、phpでは.である)
- php5.4以降は
[]
で配列を表現できる
実行順序
- ファイルの先頭から順に処理を行う
- phpコマンドで指定されたファイルの先頭がエントリポイントとなる
型システム
- 文字列と数値、日付型などがある
- 各型の相互変換は必要に応じて行われるが、想定する挙動とならないケースもあるので注意すること
例と解説
<?php // これ以降はphpのコードとして処理する
// 一行コメント
echo 'ややこしい'; // echo は関数ではないため()を書かないことが慣例
/* 複数行コメント。ここからコメントとじるまではすべてコメント
ここもコメント。実行されない。画面出力しない。
ここで閉じればここまでコメント */
// 空行は何行あっても構わない。無視される。
$string = '文字列'; // 変数$stringに文字列を代入した例
$number = 10; // $numberに数値10を代入した例
$float = 2.5; // 数値は小数も利用できる
$answer = $number / $float; // 10 / 2.5 の答え4を $answer に代入する
for($i = 0; $i < $answer; $i++){ // 制御構造の実行内容は {} 内に記述する
foo($string); //foo関数を呼び出す。制御構造内では処理単位を見やすくするためにインデント(行頭の空白)することが一般的。
bar(); //bar関数に引数を渡さずに実行する。
} // 制御構造は必ず閉じること
/* 関数は処理後の値を想定し、値の代わりに利用できる。
したがって一文中で幾つかの関数を呼び出すことも可能。
例えばfooの戻り値が4, barの戻り値が2であった場合、$x = 4 + 2という文と同義となるためである。
*/
$x = foo($number) + bar();
$y = foo(bar()); //こんな風に関数の引数に関数の処理結果を用いることもできるが、あまりやらないほうがいい
// 型変換の話
$num_string = '5'; // これは数字の5であるが、コンピュータとしては数値ではなくただの文字として認識している
$a = 3; // これは数値
$b = $a + $num_string; // しかし型変換が自動的に行われることで、8を解として得られる
$c = '3個';
$n = $b + $c; //$cで数値として解釈できない部分は無視されて、8 + 3の解11となる
$str_as_zero = '100'; // 全角の数字は罠になる
$x = 300 * $str_as_zero; // 全角の数字は数値解釈されないので、0として計算される
特殊な書き方の話
if('warrier' == $job)
この書き方は、比較するつもりで代入しないための書き方でヨーダ記法と呼ばれる。
筆者は特別この書き方を推奨しているわけではないが、このようなチュートリアル中では凡ミスを誘発させないという意味で有益である。
脚注
-
コンピュータ的には数値計算しかしていないので、数学的な関数としても正しいが、それは屁理屈に見えるだろう。 ↩
-
JSONはJavaScriptが発祥のデータ形式(JavaScript Object Notation)なので、JavaScriptが備えているのは当然としてみると、PHPがいかに「親切な」言語かわかるだろう。 ↩
-
筆者はPHPをそれほど得意としていないので、最小限の知識しか持ち合わせていない。これでは効率が悪いが、開発は続けられている。 ↩
-
この手の独自要件をそのまま「社員 番号 名前 関数」などのように検索しているツワモノもいたが、当然期待する関数は得られずにいた。 ↩
-
配列やクラス、オブジェクトなどにはまだ触れていないので、そういう分類のものがあるのだなくらいに思っていただければいい ↩ ↩2 ↩3
-
日本語がプログラムに不向きであることの一つに、検索の仕方がある。キーワードをうまく考えて検索しなければ必要な情報にたどり着きにくいためである。これが英語の場合だと「get parameter from html form by php」と勝手に分かち書きになるので、自然言語風に検索ができるとのことだ。最近はgoogle assistantなどに見られるように、自然言語検索も強くなってきたのでこの傾向は下がるのかもしれない。 ↩