web-dev-qa-db-ja.com

純粋な機能の利点

今日私は純粋な関数について読んでいて、その使用法と混同されました:

関数が同じ入力のセットに対して同じ値のセットを返し、観察可能な副作用がない場合、関数は純粋であると言います。

例えばstrlen()は純粋な関数ですが、Rand()は不純な関数です。

__attribute__ ((pure)) int fun(int i)
{
    return i*i;
}

int main()
{
    int i=10;
    printf("%d",fun(i));//outputs 100
    return 0;
}

http://ideone.com/33XJ

上記のプログラムは、pure宣言がない場合と同じように動作します。

pure [出力に変更がない場合]として関数を宣言する利点は何ですか?

81
Green goblin

pureは、関数について特定の最適化を行えることをコンパイラーに知らせます。以下のようなコードを想像してください。

_for (int i = 0; i < 1000; i++)
{
    printf("%d", fun(10));
}
_

純粋な関数を使用すると、コンパイラはfun(10)を1000回ではなく1回だけ評価する必要があることを認識できます。複雑な関数の場合、それは大きな勝利です。

144
Philip Kendall

関数が「純粋」であると言うとき、それは外部から見える副作用がないことを保証しています(そして、コメントが言うように、あなたが嘘をつくと、悪いことが起こる可能性があります)。関数が「純粋」であることを知ることは、コンパイラーに利点があります。コンパイラーは、この知識を使用して特定の最適化を行うことができます。

GCCドキュメントpure属性について言っていることは次のとおりです。

ピュア

多くの関数には戻り値以外の効果はなく、戻り値はパラメーターやグローバル変数にのみ依存します。このような関数は、算術演算子と同じように、一般的な部分式の削除とループの最適化の対象となる可能性があります。これらの関数は、属性pureで宣言する必要があります。例えば、

          int square (int) __attribute__ ((pure));

フィリップの答え は、関数が「純粋」であることを知ることがループの最適化にどのように役立つかをすでに示しています。

以下は、一般的な部分式を削除するためのものです(fooは純粋です):

a = foo (99) * x + y;
b = foo (99) * x + z;

になることができる:

_tmp = foo (99) * x;
a = _tmp + y;
b = _tmp + z;
34
ArjunShankar

実行時の利点の可能性に加えて、純粋な関数は、コードを読み取る際に、はるかに簡単に推論できます。さらに、戻り値はパラメーターの値にのみ依存することがわかっているため、純粋な関数をテストする方がはるかに簡単です。

28
Frerich Raabe

純粋でない関数

int foo(int x, int y) // possible side-effects

純粋な関数の拡張のようなものです

int bar(int x, int y) // guaranteed no side-effects

明示的な関数引数x、yのほかに、潜在的な入力として宇宙の残りの部分(またはコンピューターが通信できるもの)があります。同様に、明示的な整数の戻り値の他に、コンピューターが書き込み可能なものはすべて、暗黙的に戻り値の一部です。

純粋ではない関数よりも純粋な関数について推論する方がはるかに簡単な理由は明らかです。

15
Giorgio

アドオンと同じように、C++ 11はconstexprキーワードを使用してやや体系化していることを述べておきます。例:

#include <iostream>
#include <cstring>

constexpr unsigned static_strlen(const char * str, unsigned offset = 0) {
        return (*str == '\0') ? offset : static_strlen(str + 1, offset + 1);
}

constexpr const char * str = "asdfjkl;";

constexpr unsigned len = static_strlen(str); //MUST be evaluated at compile time
//so, for example, this: int arr[len]; is legal, as len is a constant.

int main() {
    std::cout << len << std::endl << std::strlen(str) << std::endl;
    return 0;
}

Constexprの使用に関する制限により、関数は純粋であることを証明できるようになります。このようにして、コンパイラーはより積極的に最適化し(末尾再帰を使用してください!)、実行時ではなくコンパイル時に関数を評価できます。

したがって、あなたの質問に答えると、C++を使用している場合(私はCと言ったのはわかっていますが、それらは関連しています)、正しいスタイルで純粋な関数を書くと、コンパイラーは関数であらゆる種類の素晴らしいことを実行できます: -)

7
Robert Mason

一般に、純粋な関数には、コンパイラーが利用できる不純な関数よりも3つの利点があります。

キャッシング

100000回呼び出されている純粋な関数fがあるとします。これは確定的であり、そのパラメーターにのみ依存するため、コンパイラーは値を1回計算し、必要に応じて使用できます。

並列処理

純粋な関数は、共有メモリの読み取りまたは書き込みを行わないため、予期しない結果を招くことなく、別のスレッドで実行できます

参照渡し

関数f(struct t)は、引数tを値で取得します。一方、コンパイラは、宣言されている場合、tfへの参照で渡すことができます。 tの値が変更されず、パフォーマンスが向上することを保証しながら純粋


コンパイル時の考慮事項に加えて、純粋な関数は非常に簡単にテストできます。それらを呼び出すだけです。

オブジェクトを構築したり、DB /ファイルシステムへの接続を模擬したりする必要はありません。

4
Uri Goren