プログラミングでは、関数をよく使います。関数を作ったり使ったりすることが、主な作業内容と言ってもよいくらいです。関数を十分に使いこなせれば、プログラミングを習得したも同然です。
この記事では、数学的な関数から始めて、プログラミングにおける関数の意味、その作り方や使い方を解説します。最後に、関数を中心に据えた関数型プログラミングについて触れます。
関数とは
関数という言葉は、元々数学用語です。中学や高校の数学で習うので、知っている人も多いでしょう。
プログラミング用語の関数も、数学の関数から来た言葉です。ただ、厳密には数学の関数とプログラミングの関数には違いがあります。
数学的な意味での関数
中学校では、関数について、以下のようなことを習います。
「伴って変わる数x, yがあり、xが定まるとそれに対応するyがただ1つに定まるとき、yをxの関数という」
具体例で説明しましょう。
y = x + 1
という式を考えてみます。xの値が1のとき、yの値は2で、1つに定まります。xの値がなんであっても、yの値は1つだけに定まりますから、yはxの関数といえます。
何か箱のようなものがあって、入り口と出口、2つの穴が空いている。入り口に1つの値を入れると、出口から別の値が出てくる。入れる値が同じなら、出る値も同じになる。そのようなイメージです。
関数の詳しい定義は難しいのですが、ここではこういうイメージで捉えておくことにします。
プログラミングでの関数
基本的にプログラミングの関数も、ある値を与えるとそれに対応する値を1つ返すというふうになっています。
ただし、プログラミング言語の中には、「値を返さない関数」というものを作れるものが存在します。
また、多くのプログラミング言語では、「値を受け取らない関数」を作ることができます。
そして、プログラミングの関数は、その時々によって、入れる値が同じでも出る値が変化することがあります。
このように、数学的な意味での関数とは意味のズレがあります。
プログラミングにおいては、「関数に与える値」のことを「引数(ひきすう)」と呼び、「関数が返す値」のことを「返値(かえりち)」と呼びます。
引数
引数とは、「関数に与える値」のことです。つまり入力です。プログラミングにおいては、複数の引数を受け取る関数を定義することができます。また、引数を渡さない関数も作れます。また、引数のデータ型は数値に限りません。文字列その他のデータを与える関数を定義することも可能です。
返値
返値とは、「関数が返す値」のことです。つまり出力です。関数の中で何らかの処理をして、その結果を返すのです。
関数の中でreturun文を使って明示的に返値を返す言語もあれば、関数の中で最後に評価された値を返値とする言語もあります。
関数の作り方
関数を作ることを、「関数を定義する」といいます。関数定義には、各言語ごとに決まった書き方があります。
例を挙げれば、Rubyでは次のように定義します。(厳密には、Rubyでは関数ではなく「メソッド」なのですが、細かいことは置いておきます)
def plus_one(num)
num + 1
end
defが関数定義のはじまり、endが終わりを表します。plus_oneが関数名で、()の中のnumが引数です。このplus_one関数は、引数として受け取ったnumに、1を足して返します。Rubyではreturn文で明示的に返値を返す必要はありません。最後の評価値が自動的に返値になります。ここでは、関数の中身が1行しかないので、必然的に「num + 1」の値が返値になります。
これを使うと、以下のようになります。
plus_one(1) #=> 2
plus_one(3) #=> 4
plus_one(100) #=> 101
ここからは、プログラミング中のどんなときに、どうやって関数を作るかということを見ていきます。
機能をまとめる
関数を作るべきシチュエーションとしてまず挙げられるのは、「機能をまとめる」ときです。
何か一連の処理があって、それをいったん、ひとまとまりとして扱いたいというときに、それを関数として定義することがあります。
それによって、その一連の処理に関数名という名前が付くことになり、コードが読みやすくなります。
このときに、ひとつひとつの関数の中身があまり長くならないようにします。長い処理を、いくつかの関数で表現するのです。こうすることによって、その処理の流れが見通しやすくなります。処理の詳細については、関数定義のほうを読めば分かります。
重複したコードを抜き出す
プログラミングをしていると、同じような処理を複数の箇所で行うことがたびたびあります。そういうコードが何ヶ所かで出てきたら、それは関数として抜き出すチャンスです。
プログラミングでは、同じようなコードを繰り返し書くのは避けるべきです。同じ処理があちこちに散らばっていると、修正しなければならなくなったときに大変だからです。
関数名の付け方
関数を定義するときには、関数の名前を考えなければなりません。関数名は、よく吟味して適切なものを選びましょう。関数を使う大きな目的が、コードを読みやすくすることだからです。
わかりにくい関数名を付けると、コードの読みやすさが損なわれてしまいます。もし、どうしてもうまい関数名が思いつかないとき、それは関数の処理内容自体に何か問題があるのかもしれません。
関数名を考える際には、以下の点に留意しましょう。なお、プログラミングにおいて、あらゆる名前は英語で付けるのが一般的ですので、ここでは英語の名前を用いて例を示します。
機能を端的に表す
関数は「機能」です。その名前も、機能に基づかなければなりません。なるべくわかりやすく、ズバリと機能を表すような名前にするのがよい関数名です。
よくあるパターンが、動詞で始まる関数名です。「1を足す」なら「plus_one」といった具合に、その関数が「何をするのか」を端的に表します。
関数名は、動詞にこだわる必要はありません。その関数が「何を返すのか」に着目する名付け方もあります。例えば、現在の時刻を返す関数なら「current_time」のようにするのです。
いずれにしても、分かりやすく、読みやすく、あまりひねらずに付けるのがよいでしょう。
長すぎる関数名は要注意
関数名が長くなりすぎるのは、よくありません。そうかといって、単語の頭文字だけを取った関数名にするなど、極端に短縮してしまうと意味が分からなくなります。
長すぎる関数名は、関数の処理内容そのものが長すぎることに原因があるのかもしれません。例えば、1つの関数の中で大事な処理を複数行っている場合、それらを「and」で繋いだような関数名にするというケースが考えられます。
極端な例を出せば、「download_photos_and_calculate_tax」(写真をダウンロードする。そして税金を計算する)のように、まったく関連のない2つのことが関数名に入っていたりしないか確かめましょう。これは、関数の作りとしてはかなりまずいものです。
こういう場合は、関数を2つに分けて、「download_photos」と「calculate_tax」を別々にします。
ここまで極端なら分かりやすいのですが、現実にはかなり微妙な場合も多々あります。できるだけ「1つの関数には1つの機能」になるようにするのがコツです。そうすると、可読性の点からも機能の点からもよりよいコードになります。
関数の使い方
関数を使うのは、簡単です。呼び出して、引数を渡し、返値がある場合はその返値を変数に入れるなどして使うという手順です。
書き方はプログラミング言語によって異なります。大別すれば、
関数名(引数, 引数, 引数, …)
関数名 引数, 引数, 引数, …
のどちらかの形になっていることが一般的です。両方に対応している言語もあります。
引数の数は、関数ごとに決まっています。呼び出す際に、足りなかったり多すぎたりするとエラーになることがあります。中には省略可能な引数もあって、そういうときは引数の数が少なくてもエラーが出ないこともあります。
引数を省略すると、あらかじめ関数定義のところで決められている値が引数として使われることがあります。こういう引数を「デフォルト引数」といいます。
ところで、関数には「組み込み関数」や「自作関数」があります。
組み込み関数というのは、その言語にあらかじめ用意されている関数のことです。言語が提供してくれる便利な機能群です。
自作関数は、「ユーザー定義関数」ともいい、プログラマーが自ら作った関数のことです。複数の人で開発している場合は、自分以外の誰かが書いた関数のこともあります。
組み込み関数
組み込み関数は、言語によって様々なものがあります。数学的な計算をするものや、文字列操作をするものなどが一般的です。ファイル操作などの入出力も組み込み関数で行うことが多くなっています。
組み込み関数を使うときには、特に準備などは必要なく、ただ呼び出せばよいのです。
例えば、絶対値を返す「abs」という関数が、いろいろな言語に用意されています。使い方は簡単で、数値を1つ引数として渡してやればよいのです。
abs(-1)
とすれば、1が返ってきます。
言語によっては、たくさんの組み込み関数が用意されていることがあります。コーディングする際には、組み込み関数でできることは組み込み関数でやるようにしましょう。その方が手間が省けますし、自作関数より処理速度が速いことも期待できます。
組み込み関数に似たものに、標準ライブラリ内の関数があります。標準ライブラリも、言語をインストールするとついてくる機能群です。ただ、標準ライブラリは、ライブラリを読み込む1行を書かないと使えません。
自作関数
自作関数も、使い方は組み込み関数と同じです。呼び出し、引数を渡し、返値を使うです。
自作関数は、適切に作ってさえいれば、使い方が問題になることはほぼありません。もし、使ってみておかしな点があったり、思った動作と違ったりしたら、関数名の付け方などに問題はないか、確認してみましょう。
関数型プログラミング
関数についての話題の最後に、「関数型プログラミング」を紹介します。
関数型プログラミングとは、関数を主役にしたプログラミングのスタイルのことです。そういうスタイルを実現するために作られたプログラミング言語を「関数型プログラミング言語」と呼びます。
関数型プログラミングとは
「関数型プログラミング」とは、関数を主体にプログラミングすることです。ここでいう関数とは、「数学的な意味での関数」です。
数学の関数では、ある値を与えると、1つの決まった値が返ってきます。たとえば、f(x) = x + 1 という関数を考えたとき、x = 1のときは2、x = 10のときは11が、関数の値となります。これはいついかなる時でもそうなのであって、あるときはx = 1に対して3が返ってくるなどということは絶対にありません。
数学では当たり前のことなのですが、プログラミングにおける関数では、当たり前ではありません。x = 1に対して、あるときには2を返す関数が、別のときには3を返すということが起きうるのです。内部状態によって値が変化するようなコードが書けてしまうのです。
ごく簡単に言えば、こういうことを起こさないようにするのが、関数型プログラミングです。
関数型プログラミング言語
関数型プログラミングを推し進めるために作られた「関数型プログラミング言語」というものがあります。結構たくさんあるのですが、ここでは3つだけ紹介します。
LISP
LISPは、変わった表記法を取る言語です。「1 + 3 + 5」のような式を
(+ 1 3 5)
のように書きます。
(関数名 引数 引数 …)
という形でコーディングしていきます。とにかくカッコがたくさん出てきます。
Haskell
Haskellは、数学者・論理学者が作ったプログラミング言語です。「純粋」関数型プログラミング言語と呼ばれ、同じ引数には同じ返値、ということを徹底するための仕組みがいろいろあります。
Scala
Scalaは、Javaと互換性のある言語です。関数型プログラミングとオブジェクト指向に両対応する、マルチパラダイム言語です。
まとめ
プログラミングにおける関数は、機能をひとまとまりにして名前を付けたものです。関数の作り方や使い方は、コードの品質に直結します。日々、研鑽を積みましょう。