web-dev-qa-db-ja.com

「副作用」とは?

副作用の概念を明確に理解していません。

  • プログラミングの副作用は何ですか?
  • プログラミング言語に依存していますか?
  • 外的、内的副作用などはありますか?

副作用を引き起こす原因の例をいくつか挙げてください。

101
Amir Rezaei

副作用 は、ある種の状態の変更を単に指します-例えば:

  • 変数の値を変更する。
  • データをディスクに書き込む。
  • ユーザーインターフェイスのボタンを有効または無効にします。

一部の人々が言っ​​ているように思われることに反対

  • 副作用はしない非表示または予期しない必要があります(それは可能ですが、コンピューターサイエンスに適用されるため、定義とは関係ありません)。 ;

  • 副作用にはべき等性とは何もありません。べき等関数には副作用があり、非べき等関数には副作用がない場合があります(現在のシステムの日付と時刻を取得するなど)。

とてもシンプルです。副作用=何かをどこかに変える。

追伸コメンターbenjolが指摘するように、副作用の定義を pure function の定義と混同している可能性があります。これは、(a)べき等であり、(b)副作用がない-効果。一般的なコンピューターサイエンスでは、一方が他方を意味するわけではありませんが、関数型プログラミング言語は通常、両方の制約を強制する傾向があります。

114
Aaronaught

コンピュータの状態を変更したり、外界と相互作用したりする操作には、副作用があると言われています。 副作用 のWikipediaを参照してください。

たとえば、この関数には副作用がありません。その結果は入力引数にのみ依存し、プログラムの状態や呼び出されたときの環境は変わりません。

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); }
38

既存の答えはかなり良いと思います。 IMOが十分に強調されていないいくつかの側面について詳しく説明したいと思います。

数学では、関数は値のタプルから値への単なるマッピングです。したがって、関数fと値xを指定すると、f(x)は常に同じ結果yになります。式のどこでもf(x)yに置き換えることができ、何も変更されません。

多くのプログラミング言語で関数(またはプロシージャ)と呼ばれるものは、次の理由で実行できる構文(コードの一部)です。

  1. 数学的な意味で関数を計算します。つまり、指定された入力値、結果を返します。
  2. それはいくつかの効果を生み出します。画面に何かを出力し、データベースの値を変更し、ミサイルを発射し、10秒間スリープし、SMSを送信します。

したがって、効果は状態に関連するだけでなく、ミサイルの発砲や数秒間の実行の一時停止などの他の側面にも関連する可能性があります。

副作用という用語は否定的に聞こえるかもしれませんが、通常、関数を呼び出すことの効果は、関数自体の目的そのものです。関数という用語はもともと数学で使用されていたため、値の計算は関数の一次効果と見なされ、その他の効果は副作用と見なされると思います。一部のプログラミング言語では、数学的な意味での関数との混同を避けるためにprocedureという用語を使用しています。

ご了承ください

  1. 一部のプロシージャは、戻り値と副作用の両方に役立ちます。
  2. 一部のプロシージャは結果値のみを計算し、他の影響はありません。数学的な意味で関数を計算するだけなので、これらは純粋関数と呼ばれることがよくあります。
  3. いくつかの手順、例えばPythonのsleep()は、その(副次的な)効果に対してのみ役立ちます。これらは、特別な値None、またはunitまたは()または...を返す関数としてモデル化されることが多く、計算が正しく終了したことを示します。
24
Giorgio

副作用とは、操作が、意図した使用法の範囲外の変数/オブジェクトに影響を与える場合です。

グローバル変数を変更する副作用がある複雑な関数を呼び出すと、それが呼び出された理由ではなかったとしても(データベースから何かを抽出するために呼び出された可能性があります)、これが発生する可能性があります。

私は完全に不自然に見えない単純な例を思い付くのに問題があることを認めます、そして私が取り組んだものの例はここに投稿するには長すぎます)。

(しばらく前に)見た1つの例は、接続が閉じた状態の場合にデータベース接続を開く関数でした。問題は、関数の最後で接続を閉じることになっていたが、開発者がそのコードを追加するのを忘れていたことでした。したがって、ここには意図しない副作用がありました:プロシージャの呼び出しはクエリのみを実行することになっていて、副作用は接続が開いたままであり、関数が続けて2回呼び出された場合、接続がすでに開いているというエラーが発生します。


わかりました、それで皆が今例を挙げているので、私もそう思うと思います;)

/*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には、一部の計算の結果を返すprimary効果と、グローバル変数を変更する可能性があるside効果があります。

もちろん、whichが主であり、どちらが副作用であるかは解釈の余地があり、実際の使用法に依存する可能性があります。グローバルを変更する目的でこの関数を呼び出し、戻り値を破棄した場合、グローバルの変更が主な効果だと思います。

コンピュータサイエンスでは、関数または式は、状態を変更したり、呼び出し元の関数や外部の世界と観察可能な相互作用をしたりすると、副作用があると言われています。

Wikipedia-Side Effect から

数学的な意味での関数は、入力から出力へのマッピングです。関数を呼び出すことの意図された効果は、それが返す出力に入力をマッピングすることです。関数が他に何かをする場合、それは何でも問題ではありませんが、入力に出力をマッピングしない動作がある場合、その動作は副作用として知られています。

より一般的な用語では、副作用は、構造の設計者の意図した効果ではない任意の効果です。

エフェクトは、俳優に影響を与えるものです。ガールフレンドに分割テキストメッセージを送信する関数を呼び出すと、俳優、私、彼女、携帯電話会社のネットワークなどに影響します。副作用のない関数を呼び出すことで意図されている唯一の効果は、関数に対してです。入力からマッピングを返す。したがって:

   public void SendBreakupTextMessage() {
        Messaging.send("I'm breaking up with you!")
   }

これが関数であることを意図している場合、それがすべき唯一のことはvoidを返すことです。副作用がなければ、実際にテキストメッセージを送信するべきではありません。

ほとんどのプログラミング言語では、数学関数の構造はありません。そのようなものとして使用することを意図した構成はありません。そのため、ほとんどの言語ではメソッドまたはプロシージャがあると言われています。設計上、これらはさらに多くの効果を実行できるようになっています。一般的なプログラミング用語では、メソッドやプロシージャの意図については誰も気にしませんしたがって、この関数に副作用があると誰かが言ったとき、それらは事実上、この構成が動作しないことを意味します数学関数のように。そして、誰かがこの関数に副作用がないと言ったとき、それらは、この構成体が数学関数のように効果的に動作することを意味します。

純粋な関数は、定義により、常に副作用がありません。純粋な関数とは言い方を変えると、この関数は、より多くの効果を許可する構造を使用していても、数学関数と同じ効果しか持たないということです。

副作用のない機能が純粋ではない場合は、私に誰にでも教えてください。純粋および副作用なしという用語を使用する文のコンテキストの主な意図された効果が、関数の数学的に意図された効果の効果ではない場合を除き、これらは常に等しくなります。

そのため、まれにではありますが、これは、受け入れられた回答に人が不足していること、また人を誤解していること(最も一般的な仮定ではないため)であると私は考えていますが、プログラミング関数の意図した効果が入力を出力にマップします。入力は関数の明示的なパラメーターに制約されませんが、出力は明示的な戻り値に制約されます。それが意図した効果であると想定した場合、意図した効果の他の場所からの入力を許可したので、ファイルを読み取り、ファイルの内容に基づいて異なる結果を返す関数は副作用がありません。

では、なぜこれがすべて重要なのですか?

それはすべて制御とそれを維持することです。関数を呼び出し、それが何か他のことを行ってから値を返す場合、その動作について推論することは困難です。関数の内部を調べて、実際のコードが何をしているかを推測し、その正確性をアサートする必要があります。理想的な状況は、関数が使用している入力が非常に明確でわかりやすく、何も実行せずに出力を返すことです。これを少しリラックスして、使用している入力を正確に知ることは、他に何も実行していないことを確認するほど役立つわけではないため、値を返すことに気づかない可能性があるため、強制するだけで十分であると言えます。それは何もしないので、どこから取得したかに関係なく、入力を出力にマップします。

ほとんどすべての場合、プログラムのポイントは、入ってくるものを出てくるものにマッピングする以外の効果を持つことです。副作用を制御するアイデアは、コードを理解しやすく、推論しやすい方法で編成できるということです。すべての副作用をまとめて、非常に明示的で中心的な場所に配置すると、これが起こっているすべてのことであり、これ以上ないことを確認して信頼する場所を簡単に知ることができます。入力も非常に明示的である場合、さまざまな入力の動作をテストするのに役立ちます。多くのさまざまな場所で入力を変更する必要がないため、わかりにくい場合もあります。あなたが欲しいものを得るために。

プログラムの動作を理解し、推論し、制御するのに最も役立つのは、すべての入力を明確にグループ化して明示し、すべての副作用をグループ化して明示することです。これは、一般的に人々が言うときに話していることです副作用、純粋など.

最も役立つのは副作用とその明示性のグループ化であるため、人々はそれを単に意味し、純粋ではなく「副作用」がないと言ってそれを区別する場合があります。しかし、副作用は想定された「意図された主要な影響」に関連しているため、文脈的な用語です。私が見つけたこれはそれほど頻繁に使用されませんが、驚くべきことに、このスレッドで多くのことについて話されています。

最後に、べき等とは、同じ関数を使ってこの関数を何度も呼び出すことで(どこから来たかは関係ありません)、常に同じ結果(副作用があるかどうか)になることを意味します。

3
Didier A.

プログラミングの副作用は、プロシージャがそのスコープの外から変数を変更するときです。副作用は言語に依存しません。副作用を排除することを目的とする言語のクラス(純粋な関数型言語)はいくつかありますが、副作用が必要なものがあるかどうかはわかりませんが、私は間違っている可能性があります。

私の知る限り、内部および外部の副作用はありません。

2
indyK1ng

以下に簡単な例を示します。

int _totalWrites;
void Write(string message)
{
    // Invoking this function has the side effect of 
    // incrementing the value of _totalWrites.
    _totalWrites++;
    Debug.Write(message);
}

副作用の定義はプログラミングに固有のものではないので、単にあなたの薬の副作用やあまりにも多くの食物を食べることを想像してください。

0
ChaosPandion