순수 기능의 이점


82

오늘 나는 순수한 기능에 대해 읽고 그 사용법과 혼동했습니다.

동일한 입력 세트에 대해 동일한 값 세트를 반환하고 관찰 가능한 부작용이없는 함수는 순수하다고합니다.

예를 들어 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/33XJU

위의 프로그램은 pure선언 이 없을 때와 같은 방식으로 작동합니다 .

함수를 pure[출력에 변화가없는 경우] 로 선언하면 어떤 이점이 있습니까?


7
예-생성 된 어셈블리를보십시오.
Philip Kendall

4
printf예를 들어, 순도에 대한이 정의가 정확하다고 생각하지 않습니다. 예를 들어 자격이 주어집니다 (동일한 인수로 두 번 호출하면 동일한 반환 값이 생성됨).하지만 순수하지는 않습니다.
tdammers

14
@tdammers : 사실, ...and no side-effects...부품 이 부족합니다 .
Frerich Raabe

2
@Ben : 엔트로피는 어디에서 왔습니까? 우리는 여기서 (이론적으로) 결정 론적 기계를 다루고 있습니다. 진정한 엔트로피를 얻는 유일한 방법은 부작용을 의미하는 외부 소스에서 오는 것입니다. 물론 우리는 프로그래밍 언어가 비 결정적 함수를 정의하도록 허용 할 수 있으며, 기술적 부작용이없고 함수가 실제로 비 결정적이라고 가정합니다. 하지만 그렇게하면 순도 추적의 실질적인 이점 대부분이 사라집니다.
tdammers

3
tdammers가 정확합니다- 위에 주어진 순수 의 정의 가 잘못되었습니다. Pure는 출력 이 함수에 대한 입력 에만 의존 함을 의미합니다 . 또한 관찰 가능한 부작용이 없어야합니다. "동일한 입력에 대해 동일한 출력"은 이러한 요구 사항을 매우 부정확하게 요약 한 것입니다. en.wikipedia.org/wiki/Pure_function
Dancrumb

답변:


145

pure 컴파일러에게 함수에 대한 특정 최적화를 수행 할 수 있음을 알려줍니다. 다음과 같은 코드를 상상해보십시오.

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

순수 함수를 사용하면 컴파일러는 fun(10)1000 번이 아니라 한 번만 평가해야한다는 것을 알 수 있습니다 . 복잡한 기능의 경우 큰 승리입니다.


즉, 당신은 안전하게 메모이 제이션 사용할 수 있습니다
조엘 Coehoorn

@mob 무슨 말이야? 왜 안돼?
Konrad Rudolph

15
입력 (문자열이 시작되는 주소에 대한 포인터)을 수정하지 않고 문자열 (일부 주소에서 시작하는 문자의 시퀀스)을 수정할 수 있기 때문에, 즉 메모 할 수 없습니다. 변경 불가능한 문자열 (예 : Java)을 사용하는 언어의 순수 함수일뿐입니다.
mob

5
@KonradRudolph : 1000 개의 문자열 길이를 상상해보십시오. strlen그것을 부르 십시오. 그리고 다시. 그래요? 이제 두 번째 문자를 \0. strlen지금도 여전히 1000을 반환 합니까 ? 시작 주소는 동일하지만 (== 입력이 동일 함) 함수는 이제 다른 값을 반환합니다.
Mike Bailey

5
@mob 그것은 좋은 반대입니다. 분명히 당신이 옳습니다. 나는 책조차도strlen (GCC / glibc에서) 실제로 순수 하다고 주장한다는 사실에 오도했습니다 . 그러나 glibc 구현을 살펴보면 이것이 잘못된 것으로 나타났습니다.
Konrad Rudolph

34

함수가 '순수하다'고 말하면 외부에서 눈에 보이는 부작용이 없음을 보장하는 것입니다 (댓글에서 말했듯이 거짓말을하면 나쁜 일이 발생할 수 있음). 함수가 '순수'라는 것을 알면 컴파일러에 이점이 있으며이 지식을 사용하여 특정 최적화를 수행 할 수 있습니다.

다음은 GCC 문서 에서 pure속성 에 대해 말하는 내용입니다 .

순수한

반환 값을 제외하고 많은 함수는 효과가 없으며 반환 값은 매개 변수 및 / 또는 전역 변수에만 의존합니다. 이러한 함수는 산술 연산자처럼 공통 하위 표현식 제거 및 루프 최적화의 대상이 될 수 있습니다. 이러한 함수는 pure 속성으로 선언해야합니다. 예를 들면

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

Philip의 대답은 이미 함수가 '순수'라는 것을 아는 것이 루프 최적화에 어떻게 도움이 될 수 있는지 보여줍니다.

다음은 일반적인 하위 표현식 제거를위한 것입니다 (주어진 foo것은 순수함).

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

다음이 될 수 있습니다.

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

3
이 작업을 수행하는지 확실하지 않지만 순수 함수를 사용하면 함수가 호출 될 때 컴파일러가 다시 정렬 할 수 있습니다. 부작용 가능성이있는 경우 컴파일러는 더 보수적이어야합니다.
mpdonadio

@MPD-예, 합리적으로 들립니다. 그리고 이후 call명령은 슈퍼 스칼라 CPU에 대한 병목 현상이 컴파일러의 도움이 도움이 될 수 있습니다.
ArjunShankar

몇 년 전에이 기술을 사용하여 조만간 반환 값을 얻었던 DSP 컴파일러를 사용했던 것을 모호하게 기억합니다. 이를 통해 파이프 라인 중단을 최소화 할 수있었습니다.
mpdonadio

1
99는 const이고 foo는 항상 같은 결과를 반환하므로 "foo (99)"를 미리 계산할 수 있습니까? 아마도 2 단계 컴파일에서?
markwatson

1
@markwatson-잘 모르겠습니다. 단순히 불가능한 경우가있을 수 있습니다. 예를 들어 foo다른 컴파일 단위 (다른 C 파일)의 일부이거나 사전 컴파일 된 라이브러리에있는 경우. 두 경우 모두 컴파일러는 무엇을하는지 알지 foo못하며 미리 계산할 수 없습니다.
ArjunShankar 2012-06-25

29

가능한 런타임 이점 외에도 순수 함수는 코드를 읽을 때 추론하기가 훨씬 쉽습니다. 또한 반환 값이 매개 변수의 값에만 의존한다는 것을 알고 있기 때문에 순수 함수를 테스트하는 것이 훨씬 쉽습니다.


3
+1, 테스트에 대한 귀하의 요점은 흥미로운 것입니다. 설정 및 해체가 필요하지 않습니다.
ArjunShankar

15

순수하지 않은 기능

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

순수한 기능의 확장과 같습니다.

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

여기에서 명시적인 함수 인수 x, y 외에 우주의 나머지 부분 (또는 컴퓨터가 통신 할 수있는 모든 것)을 암시 적 잠재적 입력으로 가지고 있습니다. 마찬가지로 명시 적 정수 반환 값 외에 컴퓨터에서 쓸 수있는 모든 항목은 암시 적으로 반환 값의 일부입니다.

순수하지 않은 함수보다 순수 함수에 대해 추론하는 것이 왜 훨씬 더 쉬운 지 분명해야합니다.


1
+1 : 우주를 잠재적 인 입력으로 사용하는 것은 순수함과 순수하지 않음의 차이를 설명하는 아주 좋은 방법입니다.
ArjunShankar 2012-06-22

실제로 이것은 모나드의 개념입니다.
Kristopher Micinski 2010 년

7

애드온과 마찬가지로 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라고 말했지만 관련이 있음) 올바른 스타일로 순수한 함수를 작성하면 컴파일러가 함수로 모든 종류의 멋진 일을 할 수 있습니다. -)


4

일반적으로 Pure 함수는 컴파일러가 활용할 수있는 불순한 함수보다 3 가지 장점이 있습니다.

캐싱

f100000 번 호출되는 순수 함수가 있다고 가정 해 보겠습니다. 결정적이며 매개 변수에만 의존하기 때문에 컴파일러는 값을 한 번 계산하고 필요할 때 사용할 수 있습니다.

병행

순수 함수는 공유 메모리를 읽거나 쓰지 않으므로 예기치 않은 결과없이 별도의 스레드에서 실행할 수 있습니다.

참조로 전달

함수 f(struct t)t값 으로 인수 를 가져오고 반면에 컴파일러는 값이 변경되지 않고 성능 향상 을 보장하면서 순수로 선언 된 경우 t참조로 전달할 수 있습니다.ft


컴파일 시간 고려 사항 외에도 순수 함수는 매우 쉽게 테스트 할 수 있습니다. 호출하기 만하면됩니다.

개체를 구성하거나 DB / 파일 시스템에 대한 모의 연결을 만들 필요가 없습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.