回答:
副作用は単に状態のいくつかの種類の修飾を意味する-例えば:
一部の人々が言っているように思われることに反して:
副作用を隠したり予期したりする必要はありません(可能性はありますが、コンピューターサイエンスに適用される定義とは関係ありません)。
副作用は、dem等性とは関係ありません。べき等関数には副作用があり、非べき等関数には副作用がない場合があります(現在のシステムの日付と時刻の取得など)。
とても簡単です。副作用=どこかで何かを変える。
PSコメンテーターのベンジョルが指摘するように、何人かの人々は副作用の定義を純粋な関数の定義と混同しているかもしれません。一般的なコンピューターサイエンスでは、一方は他方を意味しませんが、関数型プログラミング言語は通常、両方の制約を強制する傾向があります。
++a
。割り当てのように見えません。 b = ++a;
2つの副作用があります。明白なものとの暗号割り当てa
。それは、(ある人にとっては)望ましい副作用のようなものです。しかし、私のキャリア全体が微妙にならないようにするための副作用と呼ばれています。
コンピューターの状態を変更したり、外の世界とやり取りしたりする操作には、副作用があると言われています。副作用に関するウィキペディアを参照してください。
たとえば、この関数には副作用はありません。その結果は入力引数のみに依存し、呼び出されたときにプログラムの状態や環境は変化しません。
int square(int x) { return x * x; }
対照的に、これらの関数を呼び出すと、コンピューターの状態に関する何かが変わるため、呼び出した順序に応じて異なる結果が得られます。
int n = 0;
int next_n() { return n++; }
void set_n(int newN) { n = newN; }
この関数には、出力にデータを書き込むという副作用があります。戻り値が必要なため、関数を呼び出さないでください。「外の世界」に与える効果が必要なため、これを呼び出します。
int Write(const char* s) { return printf("Output: %s\n", s); }
Write
例が示すように、副作用があるということは、関数がその入力に対してその出力を変更すること、あるいはその出力が入力にまったく依存することさえ意味しません。
square(x)
により、関数が定義されているモジュールがディスクからロードされる場合があります。これを副作用と見なすべきですか?結局のところ、これは(最初の)呼び出しはなど、そのRAMの使用率が上がる、予想外に時間がかかることをメンズ
square(x)
ために呼び出している場合、それは副作用であると考えることができます。
既存の答えはかなり良いと思います。IMOが十分に強調されていないいくつかの側面について詳しく説明したいと思います。
数学では、関数は値のタプルから値への単なるマッピングです。だから、機能与えられたf
値x
、f(x)
常に同じ結果になりますy
。あなたも交換することができるf(x)
とのy
表現にどこでも、何も変更されます。
多くのプログラミング言語で関数(またはプロシージャ)と呼ばれるものは、次の理由で実行できる構造(コード)です。
そのため、効果は状態だけでなく、ミサイルの発射や数秒間の実行の一時停止などの他の側面にも関連します。
副作用という用語は否定的に聞こえるかもしれませんが、通常、関数を呼び出すことの効果は関数自体の目的です。I用語関数は元々値があると考えられている計算、数学で使用されたため、と仮定一次効果の任意の他の効果が考慮されるのに対し、関数の副作用。一部のプログラミング言語では、手続きという用語を使用して、数学的な意味での関数との混同を避けています。
ご了承ください
sleep()
Pythonなどの一部のプロシージャは、その(副作用)効果に対してのみ有用です。これらは多くの場合、特別な値None
、unit
または()
または... を返す関数としてモデル化されます。これは、計算が正しく終了したことを単に示します。副作用とは、操作が意図した使用範囲外の変数/オブジェクトに影響を与える場合です。
グローバル変数を変更する副作用がある複雑な関数を呼び出すと、それが呼び出された理由ではありませんでした(データベースから何かを抽出するために呼び出された可能性があります)。
私は完全に不自然に見えない簡単な例を思い付くのに苦労していることを認めます、そして私が取り組んだものからの例はここに投稿するには長すぎます(そしてそれは仕事に関連しているので、とにかくおそらく)。
(少し前に)見た例の1つは、接続が閉じた状態にある場合にデータベース接続を開く関数でした。問題は、関数の最後で接続を閉じることになっていたが、開発者はそのコードを追加するのを忘れていたということでした。そのため、ここでは意図しない副作用がありました:プロシージャの呼び出しはクエリのみを行うことになっており、副作用は接続が開いたままであり、関数が続けて2回呼び出された場合、接続があったというエラーが発生しますすでに開いています。
OK、だからみんなが今例を挙げているので、私もそう思うと思う;)
/*code is PL/SQL-styled pseudo-code because that's what's on my mind right now*/
g_some_global int := 0; --define a globally accessible variable somewhere.
function do_task_x(in_a in number) is
begin
b := calculate_magic(in_a);
if b mod 2 == 0 then
g_some_global := g_some_global + b;
end if;
return (b * 2.3);
end;
機能はdo_task_x
有している一次いくつかの計算の結果を返すの効果、及びサイドおそらくグローバル変数を変更することの効果を。
もちろん、どちらが主であり、どちらが副作用であるかは、解釈の余地があり、実際の使用法に依存する可能性があります。グローバルを変更する目的でこの関数を呼び出し、返された値を破棄すると、グローバルの変更が主な効果であると言えます。
コンピューターサイエンスでは、関数または式は、何らかの状態を変更したり、呼び出し元の関数または外部の世界と観察可能な相互作用がある場合に副作用があると言われています。
関数は、数学的な意味で、入力から出力へのマッピングです。関数を呼び出すことの意図された効果は、それが返す出力に入力をマップすることです。関数が他に何かを行う場合、それは問題ではありませんが、入力を出力にマッピングしない動作がある場合、その動作は副作用であることが知られています。
より一般的な用語では、副作用は、構造の設計者の意図した効果ではない効果です。
効果は、俳優に影響を与えるものです。ガールフレンドに別れたテキストメッセージを送信する関数を呼び出すと、それは多くの俳優、私、彼女、携帯電話会社のネットワークなどに影響を及ぼします。入力からマッピングを返します。だから:
public void SendBreakupTextMessage() {
Messaging.send("I'm breaking up with you!")
}
これが関数であることを意図している場合、すべきことはvoidを返すことだけです。副作用がない場合は、実際にテキストメッセージを送信しないでください。
ほとんどのプログラミング言語では、数学関数の構成体はありません。そのようなものとして使用することを意図した構造はありません。それが、ほとんどの言語でメソッドまたはプロシージャがあると言われている理由です。設計上、これらはより多くの効果を実行できるように意図されています。一般的なプログラミング用語では、メソッドやプロシージャが何であるかを実際に気にする人はいないので、誰かがこの関数に副作用があると言うと、事実上、このコンストラクトは数学関数のように動作しません。そして、この関数には副作用がないと誰かが言うと、この構造は数学関数のように効果的に動作します。
定義により、純粋な関数には常に副作用がありません。純粋な関数とは、この関数は、より多くの効果を可能にする構造を使用しているにもかかわらず、数学的な関数の効果と同等の効果しか持たないということです。
副作用のない関数が純粋ではない場合、私は誰にでも言うように挑戦します。純粋で副作用のない用語を使用した文のコンテキストの主な意図された効果が、関数の数学的な意図された効果のものでない限り、それらは常に等しい。
そのため、まれではありますが、これは受け入れられた答えの中で人々に欠けている、誤解している(最も一般的な仮定ではない)区別であると信じていますが、プログラミング関数の意図された効果は入力を出力にマップします。入力は関数の明示的なパラメーターに制約されませんが、出力は明示的な戻り値に制約されます。それが意図された効果であると仮定した場合、ファイルを読み込んでファイルの内容に基づいて異なる結果を返す関数は、意図した効果の他の場所からの入力を許可するため、副作用がありません。
では、なぜこれがすべて重要なのでしょうか?
それはすべて制御とそれを維持することです。関数を呼び出し、何か他のことをしてから値を返す場合、その動作について推論するのは困難です。実際のコードの関数内を調べて、何をしているかを推測し、その正確性を主張する必要があります。理想的な状況は、関数が使用している入力が何であるかを非常に明確かつ簡単に知ることができ、それに対する出力を返すこと以外は何も実行しないことです。これを少しリラックスして、それが使用している入力を正確に知ることは、値を返すことに気付いていないかもしれない他のことを何もしていないことを確信するほど有用ではないと言うことができます。それは、それをどこから取得したかに関係なく、入力にマップする他のことは何もしないということです。
ほとんどすべての場合、プログラムのポイントは、入ってくるものを出てくるものにマッピングする以外の効果を持つことです。副作用を制御するという考え方は、理解しやすく、推論しやすい方法でコードを整理できるということです。非常に明示的かつ中心的な場所ですべての副作用をまとめると、これが起こっているのはこれだけであるので、どこを見れば信頼できるかが簡単にわかります。入力も非常に明示的である場合、異なる入力の動作をテストするのに役立ち、多くの異なる場所で入力を変更する必要がないため、使いやすいです。欲しいものを手に入れます。
プログラムの動作を理解、理由、制御するのに最も役立つのは、すべての入力を明確にグループ化して明示的にすることと、すべての副作用を一緒にグループ化して明示的にすることです。副作用、純粋など
最も有用なのは副作用とその明示性のグループ化であるため、人々はそれを意味するだけで、純粋ではないが「副作用」がないと言って区別する場合があります。しかし、副作用は、想定される「意図された主効果」に関連しているため、文脈上の用語です。これは、あまり頻繁には使用されていませんが、驚くことに、このスレッドでは多くのことについて語られています。
最後に、i等性とは、同じ入力を使用してこの関数を何度も呼び出すと(それらがどこから来たかは関係ありません)、常に同じ効果(副作用の有無)が発生することを意味します。
プログラミングでは、プロシージャが変数をそのスコープ外から変更するときの副作用があります。副作用は言語に依存しません。副作用を排除することを目的とする言語のクラス(純粋な関数型言語)がありますが、副作用が必要なものがあるかどうかはわかりませんが、間違っている可能性があります。
私の知る限り、内部および外部の副作用はありません。
以下に簡単な例を示します。
int _totalWrites;
void Write(string message)
{
// Invoking this function has the side effect of
// incrementing the value of _totalWrites.
_totalWrites++;
Debug.Write(message);
}
副作用の定義はプログラミングに固有のものではないので、単にあなたの薬の副作用や食べ過ぎの副作用を想像してください。
x++
が変数を変更するという事実は、x
一般的に副作用と見なされます。式の値は、のプリインクリメント値ですx
。これは、式の副作用以外の部分です。
副作用は、コード内で発生するもので、明らかではありません。
たとえば、このクラスがあるとしましょう
public class ContrivedRandomGenerator {
public int Seed { get; set; }
public int GetRandomValue()
{
Random(Seed);
Seed++;
}
}
最初にクラスを作成するときに、シードを与えます。
var randomGenerator = new ContrivedRandomGenerator();
randomGenerator.Seed = 15;
randomGenerator.GetRandomValue();
内部を知らず、ランダムな値を取得することを期待し、randomGenerator.Seedがまだ15であることを期待しますが、そうではありません。
関数呼び出しには、シード値を変更する副作用がありました。