手続き型言語の定義は、「記述された命令を逐次的に実行し、処理の結果に応じて変数の内容を変化させていくプログラミング言語」となっていて、関数型言語の定義は、「数学的な言語仕様をもつプログラミング言語のこと。一度値を与えられた変数は常にその値を維持し、計算は計算結果を引数とした関数呼び出しの繰り返しとして行われる。」とあります。
関数型の、「一度値を与えられた変数は常にその値を維持し」はどういう意味ですか?
例えば、a=2とした後に、a=3などとすればaの値は変わっているのですが。
簡単な例で説明してください。
No.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 等で勉強してください。
No.4
- 回答日時:
再度 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)に変更できると思うのですが、こういう意味ではないのですか?
No.3
- 回答日時:
まず最初に「関数型」というのは精神論だと思ってください。
実用的な言語の大半は、たとえそれが関数型言語と言われていても、純粋な関数という考え方「だけ」でプログラムできるようになっていません(理由は後述)。例を挙げましょう。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の値をいちいち追う必要がなくなるからデバッグがしやすくなるということがメリットになってるんですね。
No.2
- 回答日時:
関数型言語の厳密な定義から言うと、「代入」(破壊的代入)という操作そのものが存在しません。
ですから> a=2とした後に、a=3などとすればaの値は変わっているのですが。
ということ自体ができません。突き詰めていくと「変数」自体なくなっちゃいます(すべてが関数の戻り値になる)。
ただ、「関数型言語」に分類される言語でもいわゆる代入(破壊的代入)を
行うことのできる言語はあります(Lisp, Schemeなどなど)。ですから、
積極的に変数に値の代入を行うことなく関数の呼び出しの組み合わせでプログラミングできる言語が関数型言語であるという考え方でもいいかもしれません。
興味があれば「ラムダ計算」とかいったキーワードで探してみてください。
参考URL:
関数型言語 - Wikipedia
http://ja.wikipedia.org/wiki/%E9%96%A2%E6%95%B0% …
ラムダ計算 - Wikipedia
http://ja.wikipedia.org/wiki/%E3%83%A9%E3%83%A0% …
なぜ関数プログラミングは重要か
http://www.sampou.org/haskell/article/whyfp.html
No.1
- 回答日時:
関数型言語の中で引数の値は変更してはいけないし、
もし変更してもそれは反映されないと言うことです。
C言語は関数型言語ではありませんが関数型プログラミングが可能です。
例えば(ポインターを使わずに)a=2を引数として関数xを呼んだとき
xの中でa=3を実行した場合、xの中ではこれは有効ですが
returnするとa=2に戻ります。
この方式はcall by value(値呼び出し)と言われ呼び出し元がaはそのままにして
aのコピーを作ってxに渡すのです。
関数にミスがあったとき大事なデータを壊されないためです。
> 一度値を与えられた変数は常にその値を維持し、
> 計算は計算結果を引数とした関数呼び出しの繰り返しとして行われる。
上記のことを言っていると思います。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- Visual Basic(VBA) Excel のユーザー定義関数でソルバーが動作しない 1 2022/09/05 19:51
- C言語・C++・C# C言語初心者 ポインタについて、お助けください、、 2 2023/03/15 23:50
- C言語・C++・C# C言語初心者 構造体 課題について 2 2023/03/10 19:48
- Ruby 初心者プログラミング 3 2022/10/12 11:31
- 数学 モデルのパラメータの定義がいまいちわかりません。 3 2022/10/11 15:16
- C言語・C++・C# C言語 3 2022/10/04 15:07
- その他(プログラミング・Web制作) プログラミングって本来数学的な計算をする為のものではないのですか? 学校で配られたFortran90 11 2022/08/25 22:14
- 中学校 理科だけが極端に苦手 5 2022/09/10 14:18
- HTML・CSS HTMLの・要素・属性・属性値 はプログラム言語の「変数」みたいに変更できますか? 5 2022/10/04 05:27
- C言語・C++・C# C言語初心者です、、、お助けください 2 2023/03/14 20:08
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
DWORDの実際の型は何でしょうか
-
visualstudio C# テキストボッ...
-
変数の型を定義しなかった場合...
-
関数の実体定義にヘッダファイ...
-
typedef enumの使い方を教えて...
-
long型の定数の末尾にLを付ける...
-
C++のfor文について
-
main.c:7:43: warning: implici...
-
C++/CLIでネイティブの構造体を...
-
2重定義って??
-
ハンドルされていない例外が発...
-
構造体の要素すべてに対する四...
-
構造体の宣言でエラーが出ます。
-
コンパイルすると error C1083 ...
-
C++デバックエラーについて詳し...
-
VC6でlong longでエラー?
-
スレッドのスケジューリングポ...
-
HINSTANCEの型
-
STL vectorの初期化
-
C++ template operator T()
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
DWORDの実際の型は何でしょうか
-
typedef enumの使い方を教えて...
-
C++のfor文について
-
long型の定数の末尾にLを付ける...
-
2重定義って??
-
関数の実体定義にヘッダファイ...
-
main.c:7:43: warning: implici...
-
変数の型を定義しなかった場合...
-
visualstudio C# テキストボッ...
-
ハンドルされていない例外が発...
-
C++でboolにintの値を代入する...
-
enumについて
-
構造体の宣言でエラーが出ます。
-
【#define】 defineで定義した...
-
C++ クラスをメンバにもつクラ...
-
0除算を判定したい
-
構造体の要素すべてに対する四...
-
値を返り値に返すのと参照渡し...
-
namespace定義の使い方
-
GCCで暗黙の型変換の警告を出し...
おすすめ情報