Programmierkurs
für Naturwissenschaftler/innen

関数

関数とは何か

プログラミングの文脈での関数とは通常、ある機能の単位を指す。 と言っても分かりにくいであろうから、具体的な例を見てみよう。

プログラミングをしていると、似たような作業を行う場合が良くある。 それを毎回書くと、どこかで間違ってしまうかもしれないし、プログラム全体の見通しも悪くなる。

簡単な例で見てみよう。

let sum;

sum = 1 + 2 + 3;
console.log("Sum of 1 ... 3 is ", sum);

もしこの計算が一回だけならこれで問題ないが、何回も出てくる場合はどうだろうか。

let sum;

sum = 1 + 2 + 3;
console.log("Sum of 3 numbers from 1 is ", sum);

sum = 5 + 6 + 7;
console.log("Sum of 3 numbers from 5 is ", sum);

sum = 10012 + 10103 + 10014;
console.log("Sum of 3 numbers from 10012 is ", sum);

数字は違うものの、ほとんど同じことを三回書いている。

問題は、

  • 単純に面倒くさい

  • どこかに間違いがあったら、三箇所直さなくてはならない

    • この例では全体が見えるので見落しにくいが、この処理がプログラムのあちこちに散らばっていたら、全てをもれなく修正するのは結構難しい

  • 2 番目とほぼ同じだが、後でより良い方法で書き直したくなったときに、全部直すのは面倒

ところで、上のプログラムに間違いがあることに気付いただろうか? もういちど見て、探してみよう。 答は最後に書く。

このような「動くけど結果が正しくない (しかもそれっぽい結果が出ている)」バグは発見が大変難しい。 こうした問題を避けるためにも、似たような処理は一箇所でやらせたい。

そこで、この処理を一つの機能として実装する。

function sum3(num_from) {
  let sum;
  sum = num_from + (num_from + 1) + (num_from + 2);
  console.log("Sum of 3 numbers from", num_from, "is", sum);
}

sum3(1);
sum3(5);
sum3(10012);

ここでは sum3 という関数を定義している。 この関数は値を一つ受け取る (num_from)。 これを「引数(ひきすう)」という。 この関数は num_from + (num_from + 1) + (num_from + 2) を計算し、それを表示する。

このように書くと、間違いを起こしにくくなる。 また間違いがあってもこの関数を使っている全ての箇所で間違いが発生するはずなので、発見の可能性が高まる。 さらに、例えばメッセージの部分を変えたいと思ったとき、元のプログラムでは 三箇所も直さなくてはならないが、新しい方では一箇所直せばすむ。

複数回、似たような処理をしているときは、関数としてまとめることを検討すべきである

細かいことはこの後で説明するが、まずは関数を使ってみよう。

上のプログラムを入力し、実行してみよ。 大雑把にそれぞれの行でなにをやっているか想像してみよ。

実行結果

補足
ここでは説明のために上のように書いたが、実際には (状況にもよるが) sum3 関数には計算のみに専念させた方が良い。 この例では sum3 関数の中で結果表示までしてしまっており、 sum3 関数に複数の役割を持たせてしまっている。 原則として (例外はたくさんあるものの) 一つの関数は一つの仕事に専念させることで、修正や改良がやりやすくなることが多い。

なお、 sin 等の本来の(数学的な)関数は、プログラミングの文脈では「数学関数」と呼ぶことが多い。 これについてはこのページの後半で述べる。

関数を作る

では、改めて関数の書きかたについて学ぼう。 すでにおおよそ予想がついているとは思うが、以下のように書く。

宣言・定義

JavaScript では関数の宣言・定義は同時に行われるので、これらの語はあまり厳密には区別されない。

functionname という名前の関数を宣言・定義するには、以下のように書く。

function functionname(arg1, arg2, ...) {
  // ここに処理を書く
}

最初の function はそのように書く。変数の場合の let のようなものである。

functionname は関数の名前である。 命名規則は変数と同様である。 好きなように付けてよいが、原則として、長くても分かりやすい名前を付けるのが良い。 既に使っている変数と同じ名前を使うことはできない。

arg1, arg2, … は引数と呼ばれる変数である。 これも好きに名前を付けて良い。 この変数は この関数の中でのみ使える ものである。 これについてはスコープで詳しく見る。 引数の数は任意である。 引数が必要なければ、単に書かずにおけばよい。 例えば、

function some_function_without_arguments() {
  // 引数を取らない関数
}

function some_function_with_one_argument(arg) {
  // 引数を一つ取る関数
}

function some_function_with_two_arguments(arg1, arg2) {
  // 引数を二つ取る関数
}

などのようになる。

関数が呼ばれた時に実行する内容を {} の間に書く。 {} に囲まれた部分を ブロック という。

返り値

返り値を返したい場合は、 return を使う。

例えば、度をラジアンに変換し、その値を返す関数は、

function degree_to_radian(angle_in_degree) {
  let angle_in_radian;
  angle_in_radian = Math.PI * angle_in_degree / 180;

  return angle_in_radian;
}

のように書ける。 (ここで Math.PI は $\pi$ である。変数と値 参照)

この場合は、以下のように書くこともできる。

function degree_to_radian(angle_in_degree) {
  return Math.PI * angle_in_degree / 180;
}

返り値が不要な場合は、処理だけ書けば良い。

function say_hello() {
  console.log("Hello, world");
}

関数を使う

関数の宣言・定義自体は、そのままでは何も実行しない (let a; としても見た目には何も起こらないのと同様である)。 そのためどこかでこの関数を使う (関数を呼ぶ、という言い方をすることが多い) 必要がある。

が、これもほぼ自明であろう。 上で定義した say_hello() を使いたければ、

say_hello();

と書けばよい。

返り値を使うには、それを直接使うか、一度変数に入れるかする。

let angle;
angle = degree_to_radian(45);
// let angle = degree_to_radian(45); と書いてもよい

console.log(angle);

この場合は、次のように書くこともできる。

console.log(degree_to_radian(45));

完全なプログラムとして書くと、次のようになる。

function degree_to_radian(angle_in_degree) {
  let angle_in_radian;
  angle_in_radian = Math.PI * angle_in_degree / 180;

  return angle_in_radian;
}

let angle = degree_to_radian(45);
console.log(angle);

$x$ を引数に取り、$x^2 - 2$ を計算してその値を返す関数を書け。 また、この関数を使って、$x = 0, 1, 2$ のときのこの式の値を計算し、表示せよ。

実行結果

注意

JavaScript では、異なる数の引数を入れてもエラーにならない。 これはバグの温床になりがちであるが、プログラマーが気を付けるしかない。

function test() {
  console.log("test");
}

test();
test(1);	// この引数は意味がないが、エラーにはならない
test(1, 2);

関数宣言 ・定義の場所

変数の場合は最初に使われるより前に let で宣言しなければならなかったが、 JavaScript では関数の宣言・定義は最初に使われるより前でも後でもよい。 つまり、

function say_hello() {
  console.log("Hello, world");
}

say_hello();

say_hello();

function say_hello() {
  console.log("Hello, world");
}

はどちらも問題なく動く。

関数として書くことのメリット

関数として書くことのメリットとして、関数単体での改良をしやすいという点がある。

例えば、 sum10() という関数を次のように書いたとしよう。

function sum10(num_from) {
  let sum;

  sum = num_from + (num_from + 1) + (num_from + 2)
    + (num_from + 3) + (num_from + 4) + (num_from + 5)
    + (num_from + 6) + (num_from + 7) + (num_from + 8)
    + (num_from + 9);

  return sum;
}

console.log(sum10(10));
console.log(sum10(100));

ところが良くみると、次のようにすっきり書くことができることに気付く。

function sum10(num_from) {
  let sum = 10 * num_from + 45;
  return sum;
}

console.log(sum10(10));
console.log(sum10(100));

ポイントは、 プログラムの 関数以外の部分を変更することなく sum10() を改良できた、という点である。 上の例は、いかにも例のために作ったものであるから実感がわきにくいかもしれないが、このような場面はよくある。

ただし、「関数以外を変更することなく」改良するためには、引数と返り値が変わらない場合に限る。 従って:

関数を書く前に、引数と返り値について良く検討すべきである
と言える。

とは言え、これはあくまで理想論である。 どのような引数・返り値にすべきかの決定は経験によるところも大きい。 関数を改良する場合に、引数も変更したくなることは良くある。 最初のうちはあまり堅苦しく考えず、どんどん気楽に変更して、少しずつコツをつかんでいってもらいたい。

慣例

(変数ではなく) 関数であることを明確にするために func() のように関数名 + () と書くことがある。 例えば「上の例では sum10 という関数を作成した」と言う変わりに「上の例では sum10() を作成した」と書くことがある。 この場合は引数を省略しているだけで、引数を取らない関数だという訳ではないので、その点だけ注意しよう。

数学関数

ここまでは、自分で新しい関数を作る方法について述べてきた。 ここでは、JavaScript に最初から用意されている関数についていくつか紹介する。

これまで使ってきた console.log() もJavaScript に最初から用意されている関数の一つである1

他にもたくさんあるのだが、ここでは数学関数についてのみ述べる。

JavaScript ではさまざまな数学関数が用意されている。 e-関数であれば、 Math.exp(x) のように使う。 Math. を付けるのを忘れないこと。

let a = Math.exp(0.5);
console.log(a);

とすると、$\mathrm{e}^{1/2}$ を計算して表示する。

同様に sin であれば Math.sin(x) のように使う。 三角関数の引数はラジアン単位であるので注意。 すでに述べたように、円周率 $\pi$ は Math.PI という名前で定数が定義されているので、これを使うことができる。

let a = Math.sin(Math.PI / 3);	// sin 60°
console.log(a);

平方根は Math.sqrt(x) である。 また e を底とする対数は Math.log(), 10 を底とする対数は Math.log10() である。

絶対値は Math.abs(x) で得られる。

詳しくは、 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/MathStatic methods を参照せよ。 (Static method が何であるかについてはここでは扱わない)

クイズの答え

sum = 10012 + 10103 + 10014;

sum = 10012 + 10013 + 10014;

となるべきである (二つ目の数字は 10103 ではなく 10013)。 もちろんどちらでも数学的には何の問題がないが、プログラマーの意図とは異なっている。

繰り返しになるが、このような「動いてそれっぽい結果を出すが正しくない」バグは発見が大変難しい。 こうしたミスをいかに減らすか (ミスを犯しにくい、ミスをしても発見しやすい書き方) が大変重要である。

まとめ

ここでは関数について学んだ。

  • 関数宣言・定義は次のように書く

    function functionname(arg1, arg2, ...) {
      // ここに処理を書く
    }
    
    • arg1, arg2, … は引数と呼ばれ、この関数の中でのみ使用できる (好きな名前を付けてよい)

    • 返り値を返すには return を使う

  • JavaScript にもともと用意されている関数もたくさんある

  • 関数を書く際に気を付けるべきことはいろいろあり、それについてもいろいろ書いたが、 最初のうちは気軽にいろいろ試してみるのが良い 。 少し慣れてきたらもう一度このページを読み直し、これらの注意点について考えてもらえると良い。


1

厳密に言うとちょっと正しくないが、ここではそのように考えておくことにしよう。