- ベストアンサー
手続き型と関数型について。
手続き型言語の定義は、「記述された命令を逐次的に実行し、処理の結果に応じて変数の内容を変化させていくプログラミング言語」となっていて、関数型言語の定義は、「数学的な言語仕様をもつプログラミング言語のこと。一度値を与えられた変数は常にその値を維持し、計算は計算結果を引数とした関数呼び出しの繰り返しとして行われる。」とあります。 関数型の、「一度値を与えられた変数は常にその値を維持し」はどういう意味ですか? 例えば、a=2とした後に、a=3などとすればaの値は変わっているのですが。 簡単な例で説明してください。
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
すいません、再度 No.3 です。説明が悪過ぎました。「入れ替える」という部分は忘れてください。 では、No.4 の例をもう少し進めましょう。 int poo( void ) { psum( 5 ); /* 代入 */ psum( 2 ); /* 5+2 */ pmul( 10 ); /* ( 5+2)*10 * return( x ); } int foo( void ) { return( fmul( fsum( 5, 2 ), 10 ) ); } というされに2つの関数を考えます。 printf( "ans=%d?n", poo() ); printf( "ans=%d?n", poo() ); とすると、最初の2回目の答えは当然違ってきますよね?同じにするためには、2回目の poo() を呼ぶ前に x=0; としなければなりません。ところが、 printf( "ans=%d?n", foo() ); の場合は何度やっても同じ答えになります。つまり、手続き型の場合(途中)結果を保存しているがために、結果がおかしくなることがる、ということを示したのです。 純粋な関数型言語では手続き型のように計算の(途中)結果を覚えておくことができないので、引数が同じであれば、常に同じ結果となりますが、手続き型の場合は、プログラム次第で(意図的に、あるいは意図に反して)どうにでもなってしまいます。逆に言えば、手続き型では(途中)結果を変数に保存することで計算を省略することができ、素早く結果を出す事が可能ですが、関数型の場合、それはできないので効率が悪い(それだけが効率が悪い原因の全てではないですが)という訳です。 関数型は数式のような「美しさ」があり、手続き型に比べバグが生じ難いという利点があるので、一部の人たちから大変支持されています。これ以上は他の回答者さんの参考 URL 等で勉強してください。
その他の回答 (4)
- chirubou
- ベストアンサー率37% (189/502)
再度 No.3 です。「デバッグがしやすくなる」というのが気になって。 別の例を見てみましょう。 int x = 0; int psum( int y ) { x += y; } int pmul( int z ) { x *= z: } という手続きに対し、以下の関数があったとします。 int fsum( int x, int y ) { return( x * y ); } int fmul( int x, int y ) { return( x * y ); } ここで、( 5 + 2 ) * 10 を計算するとすると、 psum( 5 ); /* 代入 */ psum( 2 ); /* 5+2 */ pmul( 10 ); /* ( 5+2)*10 */ という手続きに対し、関数型では、 fmul( fsum( 5, 2 ), 10 ); という記述になります。手続き型の場合、psum() や pmul() の順序を変更できてしまいます(もちろん、こうすると望みの動作にならない場合があります)が、関数型の場合、順序は変えようがありません。私はこれが関数型言語の一番の効用だと思います。 もっと複雑なプログラムになった時、手続き型の関数の深いところで大域変数の値を変更してしまい、それがもとで思わぬ結果になる(バグ)ということはよくある事です。しかしながら、純粋に関数型で記述すると、自然とそういうことは起きないようにプログラムできる訳です。
お礼
補足をありがとうございます。 一つ分からないのが、「手続き型の場合、psum() や pmul() の順序を変更できてしまいますが、関数型の場合、順序は変えようがありません。」です。 順序を変更するというのは具体的にどういうことですか? (5+2)*10なら()内を先に計算しなければならないので、psum(5)とpsum(2)は入れ替えることができますが、pmulは入れ替えることはできないと思います。 fmul( fsum( 5, 2 ), 10)はfmul( fsum( 2,5 ), 10)に変更できると思うのですが、こういう意味ではないのですか?
- chirubou
- ベストアンサー率37% (189/502)
まず最初に「関数型」というのは精神論だと思ってください。実用的な言語の大半は、たとえそれが関数型言語と言われていても、純粋な関数という考え方「だけ」でプログラムできるようになっていません(理由は後述)。 例を挙げましょう。1から10までの整数の和を求めるという場合、手続き型の場合(以下 C 言語風に記述)、 x = 0; for( i=0; i<=10; i++ ) x += i; となります。これを関数型風に書くと、 int sum10( int x ) { if( x > 0 ) return( x + sum10( x -1 ) ); return( x ); } という具合になります。関数の中で自分自身を呼び出す事を再帰といいますが、こうすることで上の for() 文の変数 i を変化させることなく、繰り返しを実現できます。 しかしながら、全ての計算を純粋な関数型言語で行うことは、非常に実行効率が悪くなり、実用的ではなくなります。このため、世の中の多くの関数型言語では、実際には変数の(2度目、3度目の)書き換えを許しています。
お礼
ありがとうございます。 分かりやすい具体例でよく理解できました! この例ではiの値をいちいち追う必要がなくなるからデバッグがしやすくなるということがメリットになってるんですね。
- sakusaker7
- ベストアンサー率62% (800/1280)
関数型言語の厳密な定義から言うと、「代入」(破壊的代入)という操作そのものが存在しません。ですから > a=2とした後に、a=3などとすればaの値は変わっているのですが。 ということ自体ができません。突き詰めていくと「変数」自体なくなっちゃいます(すべてが関数の戻り値になる)。 ただ、「関数型言語」に分類される言語でもいわゆる代入(破壊的代入)を 行うことのできる言語はあります(Lisp, Schemeなどなど)。ですから、 積極的に変数に値の代入を行うことなく関数の呼び出しの組み合わせでプログラミングできる言語が関数型言語であるという考え方でもいいかもしれません。 興味があれば「ラムダ計算」とかいったキーワードで探してみてください。 参考URL: 関数型言語 - Wikipedia http://ja.wikipedia.org/wiki/%E9%96%A2%E6%95%B0%E5%9E%8B%E8%A8%80%E8%AA%9E ラムダ計算 - Wikipedia http://ja.wikipedia.org/wiki/%E3%83%A9%E3%83%A0%E3%83%80%E8%A8%88%E7%AE%97 なぜ関数プログラミングは重要か http://www.sampou.org/haskell/article/whyfp.html
お礼
ありがとうございます。 参考ページを見て勉強してみます。 ご親切に感謝します。
- ymmasayan
- ベストアンサー率30% (2593/8599)
関数型言語の中で引数の値は変更してはいけないし、 もし変更してもそれは反映されないと言うことです。 C言語は関数型言語ではありませんが関数型プログラミングが可能です。 例えば(ポインターを使わずに)a=2を引数として関数xを呼んだとき xの中でa=3を実行した場合、xの中ではこれは有効ですが returnするとa=2に戻ります。 この方式はcall by value(値呼び出し)と言われ呼び出し元がaはそのままにして aのコピーを作ってxに渡すのです。 関数にミスがあったとき大事なデータを壊されないためです。 > 一度値を与えられた変数は常にその値を維持し、 > 計算は計算結果を引数とした関数呼び出しの繰り返しとして行われる。 上記のことを言っていると思います。
お礼
分かりやすいご説明ありがとうございました。 よく理解できました。
お礼
いろいろと分かりやすい例をありがとうございました!! もやもやしたものが少しハッキリしてきました。 本当に感謝しています。