== 関数の定義と利用 == === 関数を作る === 同じような問題が沢山ある場合を考えます。 面積を求める問題です。 A,B,C,Dの4つの問題があります。 . {{attachment:menseki10.png}} 横幅(底辺の長さ)が異なるだけで、他は同じ問題です。 Aの面積は次のようにして求めることができます。 {{{ takasa = Math.sqrt( 5*5 - 2*2 ); sankaku = 4 * takasa / 2; sikaku = 4 * 3; wa = sankaku + sikaku; }}} 1行目では、三平方の定理を使って三角形の高さを求めています。<
> 2行目で三角形の面積を、3行目で四角形の面積を求め、<
> 4行目でそれを合計して全体の面積を求めています。 この後に結果を表示する命令 {{{ print( '面積', wa ); }}} を加えればプログラムができます。 次に、Bの問題を解こうとすると、プログラムに変更が必要です。 上のプログラムで 2 や 4 となっている箇所がそうです。 数を直接書かずに、いちど名前をつけてから使うようにすれば、修正は1箇所で済みます。 {{{#!java yoko = 4; takasa = Math.sqrt( 5*5 - (yoko/2)*(yoko/2) ); sankaku = yoko * takasa / 2; sikaku = yoko * 3; wa = sankaku + sikaku; print( '面積', wa ); }}} ---- === 関数を考える === 関数はなにか引数(ひきすう)を与えて呼び出すと、答となる値を返してくれるものです。 例えば、Math.sqrt は、数値を与えるとその平方根を返す関数でした。 {{{ js> Math.sqrt(2) 1.41421356 }}} 上の問題では、横幅を与えて呼び出すと、面積を返してくれる関数があれば js> menseki(4) と入力することで答を得ることができます。 システムに最初から用意されている関数は sqrtやsinのような一般的なものだけです。 それ以外の関数は用意されていません。 でも、自分で'''関数を定義'''することができます。 必要な関数は自分でプログラムしてやればよいのです。 ---- === 関数の定義 === 関数を定義するには次のように書きます。 {{{ function menseki ( yoko ) { ... return kotae; } }}} function や ( ) { } はその通りに書きます。 menseki とある部分は'''関数名'''になります。あとでこの名前で呼び出して使います。 yoko とある部分は引数につけられる名前です。 その後ろに { があり、何行かの文が続き、 } で終わります。 { から } までの部分を関数の本体と呼びます。 ... の部分には、答を計算するための代入文などを書きます。 この部分は何行になってもかまいません。 最後に、値を返すために return文を書きます。 kotaeの部分には返す値を書きます。 全体の構文は次のようになります。 本体部分は文をいくつか並べて大括弧で囲った形です。 . {{attachment:kansuu1.png}} return文の構文はこのようになります。 . {{attachment:kansuu2.png}} ---- === 関数の動作 === 上の問題での面積を求める関数はこのように書けます。 {{{#!java function menseki ( yoko ) { takasa = Math.sqrt( 5*5 - (yoko/2)*(yoko/2) ); sankaku = yoko * takasa / 2; sikaku = yoko * 3; wa = sankaku + sikaku; return wa; } }}} 本体部分は tab を使って右にずらして記述しています。 間違いを少なくするために、このように書くように心がけましょう。 本体部分は、次の図形の面積を求める計算になっています。 値の決まっていない yoko は関数の引数に対応しています。 . {{attachment:menseki10b.png}} このプログラムをエディタで入力し、ファイル名を sample.js として保存したとします。 {{{ js> load('sample.js') }}} を行うとこのプログラムが読み込まれます。 プログラムが(文法的に)正しく記述されているときには、loadに対する反応はありません。 . {{attachment:rhino8.png}} プログラムに文法的な間違いが見つかったときだけ、エラーメッセージが表示されます。 文法的な誤りが無ければ、プログラムが読み込まれ、関数が定義されます。 関数が定義された後では、関数を使うことができます。 {{{ js> menseki(4) }}} として関数 menseki が呼び出されると、 . 1)引数として与えられた値 4 に、引数の名前である yoko と名前がつけられます。 . 2)本体部分に書かれた文が順に実行されます。 . 3)return文により、wa の値が関数 menseki の値として返されます。 の順に行われその結果、横幅が4のときの面積が求まります。 {{{ js> load('sample.js') js> menseki(4) 21.165151389911678 js> menseki(8) 36 js> menseki(2) 10.898979485566356 js> menseki(9) 36.807522622966516 }}} 一度loadを行って正しく定義が行われたあとでは、続けて何度も利用することができます。 ---- === 関数を定義し、利用する === 代入文、print文をプログラムファイル内に直接記述し、loadを行うと先頭行から順に実行されました。 また、functionを用いて関数の定義を行ったときには、 loadの後、関数を呼び出して計算させることができました。 両方をあわせて行うときには、次のような書き方になります。 . {{attachment:menseki10c.png}} 2つの図形それぞれの面積と合計を求めるプログラムです。 2つの図形は似ているので1つの関数で計算しています。 {{{#!java /* 家の形2つの面積とその和を求める 作成者:田中正彦 作成日:2007-6-6 */ function menseki ( yoko ) { takasa = Math.sqrt( 5*5 - (yoko/2)*(yoko/2) ); sankaku = yoko * takasa / 2; sikaku = yoko * 3; wa = sankaku + sikaku; return wa; } dai = menseki(8); syo = menseki(5); wa = dai+syo; print(' 大', dai); print(' 小', syo); print(' 合計', wa); }}} プログラムの先頭にはコメントをつけています。 いつも書くように心がけましょう。 次に関数の定義を書きます。 関数の名前はmensekiで、引数にはyokoと名前をつけています。 最後に関数を利用した計算などの処理を書きます。 引数を8として面積を計算した結果をdaiに、引数が5のときの結果をsyoに代入しています。 daiとsyoの和を求め、それらをprintしています。 ---- === 実行順序 === このプログラムをloadしたときにどの順に実行されるか追いかけてみます。 先頭行から順に処理されることに変わりはありません。 (1)の部分はコメントですから、この部分では何も行われません。<
> (2)の部分では関数の定義だけが行われます。本体部分はまだ実行されません。<
> (3)の部分は順に実行されます。 . {{attachment:kansuu3.png}} したがって、最初に実行されるのは(3)の先頭である15行目の . dai = menseki(8); になります。 ここで関数mensekiが呼び出され、引数の値8が引数の名前yokoと対応づけられます。(yokoの値が8になる)。そして、関数の本体部分が実行されます。 8行目から11行目の代入文が実行され、waの値は32になります。12行目のreturn文でこの値が関数の値として返され、関数本体の実行が終わります。 その結果、menseki(8)の値が32と求まり、値がdaiに代入されます。 次の16行目でも、関数 mensekiが呼び出されます。 今度は引数の値が5ですから、yokoの値を5として本体部分が実行されます。 17行目の代入文、18行目から20行目のprint文がこの順に実行されることはわかりますね。 実行順序を矢印で表すと次のようになります。 . {{attachment:kansuu4.png}} ---- === 引数の個数 === 引数の数は2つ以上でもかまいません。 引数が2つ以上のときはカンマで区切って書きます。 {{{ function name ( param1, param2, param3 ) { ... } }}} 引数の無い関数も可能です。 引数がなくても括弧は必要です。 {{{ function name ( ) { ... } }}} 関数定義での引数の個数と、関数呼び出しでの引数の個数は一致しなければなりません。 引数が3つの関数を呼び出すときは . a = name(2,4,6); 引数が0個の関数を呼び出すときは . b = name( ); のような書き方になります。 ---- === 値を返さない関数 === 普通の関数は、引数として値を与えて呼び出すと、なんらかの値を返します。 これらの関数は . a = Math.sqrt(2); . b = menseki(5); . c = Math.pow(4, 9); のような形で利用できました。 一方、値を返さない関数もあります。 これまでに使ってきた load命令、print文は実際は値を返さない関数として実現されています。 . load命令は、ファイル名を引数として呼び出されると、指定されたファイルの内容をプログラムとして読み込んで実行します。 . print文は、リテラルや式(の並び)を引数として呼び出されると、その値を表示します。 loadを行うと、関数の定義や変数の値への代入などが行われ、処理系の状態が変化します。<
> printを行ったときは、表示に変化が現れます。 返す値以外の、このような変化のことを'''副作用'''とよびます。 値を返す必要はないけれども副作用が必要なときに、値を返さない関数が使われます。 ---- === 値を返さない関数の定義 === 関数定義の形はこれまでに学んだものと同じです。 {{{ function namae ( hikisuu ) { ... } }}} ただし、関数本体内のreturn文の書き方がちょっとだけ違います。 値を返さない関数ではreturn文は {{{ return; }}} と書きます。 返す値がないので、returnの後ろに値を書きません。 違いはこれだけです。 ---- === 値を返さない関数を呼び出す === 値を返さない関数を使うには、関数呼び出しだけを記述します。 上で定義した関数 namaeをhikisuuの値を3として呼び出すには . namae(3); とします。 ---- '''例1''' 値を返さない関数の例です。 {{{#!java function rei1( x ) { print ( x * x * x ); return; } }}} 引数の値の3乗を計算して表示するだけの関数です。 {{{ js> rei1(10); 1000 }}} のよう使えますが、利用法は限られます。 こんな方法もある、とだけ覚えてください。 '''例2''' 以前作成した面積を求める関数にprint文を追加したものです。 {{{#!java function menseki ( yoko ) { var takasa,sankaku,sikaku,wa takasa = Math.sqrt( 5*5 - (yoko/2)*(yoko/2) ); sankaku = yoko * takasa / 2; sikaku = yoko * 3; wa = sankaku + sikaku; print ('面積は', wa); return wa; } }}} 関数が呼ばれたら、面積を計算し、結果をprint文を用いて表示しています。 計算した面積を値として返しています。 (1)この関数は普通 {{{ a = menseki(8); }}} のようにして使います。すると {{{ 面積は36 }}} と表示され、変数 a に値36が代入されます。 (2)求めた値を記憶しておく必要が無いなら {{{ menseki(8); }}} のようにして使うこともできます。すると {{{ 面積は36 }}} と表示されます。 JS> に対して入力したときは、さらに返された値が表示されます。 値を返すタイプの関数は、(1)、(2)どちらの使い方もできます。 '''例3''' 例2のプログラムで return文だけつぎのように修正した場合を考えます。 {{{ return; }}} 値を返さない関数になるので、(2)の使い方だけしかできません。 関数にはいろいろな書き方や使い方があることを示しました。 関数は値を返すものが普通で、値を返さないものもある、と覚えてください。 return文を書かないと、値を返さない関数になります。 必ず書くようにしましょう。 関数内でprint文が使われているかどうかは、値を返す返さないとは関係ありません。 何らかの表示をし、値を返してもかまいません。