순수 함수 : "부작용 없음"은 "동일한 입력이 주어지면 항상 동일한 출력"을 의미합니까?


84

함수를 정의하는 두 가지 조건 pure은 다음과 같습니다.

  1. 부작용 없음 (즉, 로컬 범위 변경 만 허용됨)
  2. 동일한 입력이 주어지면 항상 동일한 출력을 반환합니다.

첫 번째 조건이 항상 참이면 두 번째 조건이 참이 아닌 경우가 있습니까?

즉, 첫 번째 조건에서만 실제로 필요합니까?


3
귀하의 건물이 잘못 지정되었습니다. "입력"이 너무 광범위합니다. 함수는 두 종류의 입력이 있다고 생각할 수 있습니다. 그들의 주장과 "환경 적"/ "맥락 적". 이 두 종류의 입력을 구분하지 않으면 시스템 시간을 반환하는 함수는 순수하다고 생각할 수 있습니다 (obv가 아니더라도).
알렉산더 - 분석 재개 모니카

4
@Alexander : "순수한 함수"의 맥락에서 "입력"은 일반적으로 (프로그래밍 언어가 사용하는 메커니즘에 따라) 명시 적으로 전달되는 매개 변수 / 인수를 의미하는 것으로 이해됩니다. 그것은 "순수한 기능"의 정의의 일부입니다. 그러나 당신이 옳습니다. 정의를 염두에 두는 것이 중요합니다.
sleske

3
간단한 반례 : 전역 변수의 값을 반환합니다. 부작용은 없지만 (전역은 읽을 수만 있습니다!), 잠재적으로 매번 다른 결과를 얻을 수 있습니다. (글로벌이 마음에 들지 않으면 런타임에 호출 스택에 따라 달라지는 로컬 변수의 주소를 반환하십시오).
Peter-Monica 복원

2
"부작용"의 정의를 확장해야합니다. 당신은 순수한 방법은하지 않는 것을 말한다 생산 부작용이 있지만 순수한 방법은하지 않는 것 또한주의 할 필요가 소비 부작용은 다른 곳에서 생산.
Eric Lippert

2
@sleske 아마도 일반적으로 이해되지만 그 구별의 부족이 OP의 혼란의 정확한 원인입니다.
알렉산더 - 분석 재개 모니카

답변:


114

다음은 외부 범위를 변경하지 않지만 여전히 불순한 것으로 간주되는 몇 가지 반례입니다.

  • function a() { return Date.now(); }
  • function b() { return window.globalMutableVar; }
  • function c() { return document.getElementById("myInput").value; }
  • function d() { return Math.random(); } (당연히 PRNG를 변경하지만 관찰 가능한 것으로 간주되지 않음)

상수가 아닌 비 지역 변수에 액세스하면 두 번째 조건을 위반할 수 있습니다.

저는 항상 순도를위한 두 가지 조건을 보완 적이라고 생각합니다.

  • 결과 평가는 부수적 상태에 영향 을 주지 않아야합니다.
  • 평가 결과는 측면 상태의 영향을받지 않아야합니다.

부작용 이라는 용어 는 첫 번째, 로컬이 아닌 상태를 수정하는 기능을 나타냅니다. 그러나 읽기 작업도 부작용으로 간주되기도합니다. 작업 일 때도 쓰기 작업이 포함됩니다. 주 목적이 값에 액세스하는 것이더라도 마찬가지입니다. 예를 들어 생성기의 내부 상태를 수정하는 의사 난수 생성, 읽기 위치를 전진하는 입력 스트림에서 읽기 또는 "측정 수행"명령이 포함 된 외부 센서에서 읽기가 있습니다.


1
고마워 Bergi. 어떤 이유로 나는 부작용 이 로컬 범위 밖의 변수를 읽는 것을 포함한다고 생각 했지만 그러한 외부 변수를 작성 하면 부작용 일 뿐이라고 생각합니다 .
Magnus

17
prompt("you choose")부작용이 없다면 한 발 물러서서 부작용의 의미를 명확히해야합니다.
Holger

1
@Magnus 예, 정확히 그것이 효과가 의미 하는 바입니다 . 나는 그런 큰 관심을 기대하지 않았다뿐만 아니라 내 대답에 명확하게하려고 :-) 투표 수십 대답은 가치가 있도록 할 것입니다
BERGI

2
당신이 아는 모든 것에 대해 Math.random ()은 열 다이오드를 반환합니다. 실제로 잘못된 RNG를 사용하도록 지정되지 않았습니다.
Joshua

1
두 가지 조건 중 전자는 "효과"라고하는 반면 후자는 "코 이펙트"라고 들었습니다. 둘 다 "부작용"이고 불순합니다. f (coeffects, input)-> effects, output Coeffects는 더 넓은 환경의 변화에서 오는 입력이고, 효과는 더 넓은 환경을 바꾸는 출력입니다. 예를 들어 Elm 및 Clojurescrips는이 모델에서 작업을 다시 구성합니다.

30

순수한 함수 가 무엇인지 표현하는 "정상적인"방법 은 참조 투명성 측면에서입니다 . 함수가 참조 적으로 투명 하면 순수 합니다 .

참조 투명성 은 대략적으로 프로그램의 의미를 변경하지 않고 프로그램의 어느 지점에서나 함수 호출을 반환 값으로 대체하거나 그 반대로 바꿀 수 있음을 의미합니다.

예를 들어 C printf가 참조 적으로 투명 하다면 이 두 프로그램은 동일한 의미를 가져야합니다.

printf("Hello");

5;

다음 프로그램은 모두 동일한 의미를 가져야합니다.

5 + 5;

printf("Hello") + 5;

printf("Hello") + printf("Hello");

때문에 printf반환 문자의 수는이 경우 5, 작성.

void함수를 사용 하면 더욱 분명해집니다 . 나는 기능이있는 경우 void foo다음,

foo(bar, baz, quux);

다음과 같아야합니다.

;

foo, 아무것도 반환하지 않으므로 프로그램의 의미를 변경하지 않고 아무것도 반환 할 수 있어야합니다.

그러므로 참조 적으로 투명 하지도 printf않고 foo투명 하지도 않기 때문에 어느 것도 순수하지 않다는 것이 분명 합니다. 실제로 void함수 가 작동하지 않는 한 참조 적으로 투명 할 수 없습니다.

이 정의는 당신이 준 정의보다 훨씬 더 쉽게 처리 할 수 ​​있습니다. 또한 원하는 단위로 적용 할 수 있습니다. 개별 표현식, 함수, 전체 프로그램에 적용 할 수 있습니다. 예를 들어 다음과 같은 함수에 대해 이야기 할 수 있습니다.

func fib(n):
    return memo[n] if memo.has_key?(n)
    return 1 if n <= 1
    return memo[n] = fib(n-1) + fib(n-2)

함수를 구성하는 표현식을 분석하고 가변 데이터 구조, 즉 memo배열 을 사용하기 때문에 참조 적으로 투명하지 않아 순수하지 않다고 쉽게 결론을 내릴 수 있습니다 . 그러나 우리는 또한 기능에서 볼 수하고 있음을 알 수 있다 referentially 투명하고, 따라서 순수. 이것은 때때로 외부 순수성 이라고 불립니다 . 즉, 외부 세계에서는 순수하게 보이지만 내부적으로는 불순한 기능입니다.

이러한 기능은 여전히 ​​유용합니다. 왜냐하면 불순물이 주변의 모든 것을 감염시키는 반면 외부 순수 인터페이스는 일종의 "순도 장벽"을 구축하기 때문입니다. 불순물은 함수의 세 줄만 감염하지만 나머지 프로그램에는 누출되지 않습니다. . 이 세 줄은 전체 프로그램보다 정확성을 분석하기가 훨씬 쉽습니다.


2
이 불순물은 동시성을 가지면 전체 프로그램에 영향을 미칩니다.
R .. GitHub의 STOP 돕기 ICE

@R .. 동시성이 설명 된 피보나치 함수를 외부 적으로 불순하게 만들 수있는 방법을 생각할 수 있습니까? 난 못해. 쓰기 memo[n]는 멱등 적이며 읽기에 실패하면 CPU 주기만 낭비됩니다.
Brilliand

두 분 모두 동의합니다. 불순물 동시성 문제로 이어질 있지만이 특정 경우에는 그렇지 않습니다.
Jörg W Mittag 2019 년

@R .. 동시성 인식 버전을 상상하는 것은 어렵지 않습니다.
user253751

1
@Brilliand 예를 들어, memo[n] = ...먼저 사전 항목을 만든 다음 값을 저장할 수 있습니다. 그러면 다른 스레드가 초기화되지 않은 항목을 볼 수있는 창이 남습니다.
user253751 23:49:19

12

두 번째 조건은 첫 번째 조건보다 약한 제약 조건 인 것 같습니다.

예를 들어 보겠습니다. 콘솔에도 기록하는 함수를 추가하는 기능이 있다고 가정 해 보겠습니다.

function addOneAndLog(x) {
  console.log(x);
  return x + 1;
}

제공 한 두 번째 조건이 충족됩니다.이 함수는 동일한 입력이 주어지면 항상 동일한 출력을 반환합니다. 그러나 콘솔에 로깅하는 부작용이 있기 때문에 순수한 기능은 아닙니다.

순수 함수는 엄밀히 말하면 참조 투명성 의 속성을 만족시키는 함수입니다 . 그것이 우리가 프로그램의 동작을 변경하지 않고 생성하는 값으로 함수 애플리케이션을 대체 할 수있는 속성입니다.

단순히 다음을 추가하는 함수가 있다고 가정합니다.

function addOne(x) {
  return x + 1;
}

우리는 대체 할 수 addOne(5)6우리의 프로그램 변경됩니다 아무것도 어디서나.

대조적으로, 첫 번째 표현식은 콘솔에 어떤 내용이 기록되는 반면 두 번째 표현식은 그렇지 않기 때문에 동작을 변경하지 않고 프로그램의 어느 곳에서나 addOneAndLog(x)값으로 바꿀 수 6없습니다.

addOneAndLog(x)출력을 반환 하는 것 외에 수행 되는 이러한 추가 동작을 부작용으로 간주합니다 .


"당신이 설명한 두 번째 조건이 첫 번째 조건보다 약한 제약 인 것 같습니다." 아니요, 두 조건은 논리적으로 독립적입니다.
sleske

@sleske 당신은 잘못되었습니다. 순수와 부작용이라는 용어에 대한 명확한 정의를 제공했습니다. 이러한 제약 조건 내에서 주어진 입력에 대해 동일한 출력을 반환하는 것 외에 부작용이없는 함수는 아무것도 없습니다. 그러나 두 번째 조건이 첫 번째 조건없이 만족 될 수있는 예를 제공했습니다. 순수성의 개념을 이해하는 근본적인 개념은 참조 투명성입니다.
TheInnerLight

작은 오타 : 주어진 입력에 대해 동일한 출력을 반환하는 것 외에 부작용이없는 함수가 할 수있는 일은 없습니다 .
TheInnerLight

현재 시간을 반환하는 것과 같은 것은 어떻습니까? 부작용은 없지만 동일한 입력에 대해 다른 출력을 반환합니다. 또는 더 일반적으로 결과가 입력 매개 변수뿐만 아니라 (변경 가능한) 전역 변수에 따라 달라지는 모든 함수입니다.
sleske

2
일반적으로 사용되는 것과 다른 "부작용"정의를 사용하고있는 것 같습니다. 부작용은 일반적으로 "값을 반환하는 것 외에 관찰 가능한 효과"또는 "상태의 관찰 가능한 변화"로 정의됩니다. 예를 들어 Wikipedia , softwareengineering.SE에 대한이 게시물을 참조하십시오 . Date.now()순수하거나 참조 적으로 투명하지는 않지만 부작용이 있기 때문이 아니라 그 결과가 입력 이상의 것에 의존하기 때문에 당신은 완전히 옳 습니다.
sleske

7

시스템 외부에서 임의의 소스가있을 수 있습니다. 계산의 일부에 실내 온도가 포함되어 있다고 가정합니다. 그런 다음 함수를 실행하면 실내 온도의 임의의 외부 요소에 따라 매번 다른 결과가 생성됩니다. 프로그램을 실행해도 상태는 변경되지 않습니다.

어쨌든 내가 생각할 수있는 모든 것.


3
제 말에 따르면 이러한 "시스템 외부에서의 임의성"은 부작용의 한 형태입니다. 이러한 동작이있는 함수는 "pures"가 아닙니다.
Joseph M. Dion

2

FP 정의의 문제점은 매우 인위적이라는 것입니다. 각 평가 / 계산은 평가자에게 부작용이 있습니다. 이론적으로는 사실입니다. 이것에 대한 부정은 FP 사과가 철학과 논리를 무시한다는 것을 보여줍니다. "평가"는 어떤 지능적인 환경 (기계, 뇌 등)의 상태를 변화시키는 것을 의미합니다. 이것이 평가 프로세스의 특성입니다. 변경 없음- "미적분"없음. 그 효과는 매우 눈에 띄게 나타날 수 있습니다. CPU 가열 또는 고장, 과열시 마더 보드 종료 등입니다.

참조 투명성에 대해 이야기 할 때 이러한 투명성에 대한 정보는 전체 시스템의 작성자이자 의미 정보의 소유자로서 인간이 사용할 수 있으며 컴파일러에서는 사용할 수 없음을 이해해야합니다. 예를 들어 함수는 일부 외부 리소스를 읽을 수 있으며 서명에 IO 모나드가 있지만 항상 동일한 값을 반환합니다 (예 :의 결과 current_year > 0). 컴파일러는 함수가 항상 동일한 결과를 반환한다는 것을 알지 못하므로 함수는 불순하지만 참조 적으로 투명한 속성을 True가지며 상수 로 대체 될 수 있습니다 .

따라서 이러한 부정확성을 방지하려면 프로그래밍 언어에서 수학 함수와 "함수"를 구분해야합니다. Haskell의 함수는 항상 불순하고 그와 관련된 순도의 정의는 항상 매우 조건부입니다. 실제 하드웨어에서 실행되고 있으며 실제 부작용과 물리적 특성은 수학적 함수에 적합하지 않습니다. 이것은 "printf"함수가있는 예제가 완전히 잘못되었음을 의미합니다.

그러나 모든 수학 함수도 순수하지는 않습니다 t. 매개 변수로 (시간) 을 갖는 각 함수는 불순 할 수 있습니다. t함수의 모든 효과와 확률 적 특성을 보유합니다. 일반적으로 입력 신호가 있고 실제 값에 대해 알지 못합니다. 소음 일 수도 있습니다.


2

첫 번째 조건이 항상 참이면 두 번째 조건이 참이 아닌 경우가 있습니까?

아래의 간단한 코드 스 니펫을 고려하십시오.

public int Sum(int a, int b) {
    Random rnd = new Random();
    return rnd.Next(1, 10);
}

이 코드는 동일한 입력 세트에 대해 임의의 출력을 반환하지만 부작용은 없습니다.

당신이 언급 한 포인트 # 1과 # 2의 전체적인 효과는 다음을 의미합니다 : 같은 i / p를 가진 기능 Sum이 프로그램에서 그 결과로 대체된다면, 프로그램의 전체적인 의미는 변하지 않습니다 . 이것은 참조 투명성 일뿐 입니다.


그러나이 경우 첫 번째 조건은 확인되지 않습니다. 콘솔에 쓰는 것은 기계 자체의 상태를 변경하기 때문에 부작용으로 간주됩니다.
오른쪽 다리

그것을 지적하기 위해 @ Rightleg thx. 어떻게 든 OP를 완전히 다른 방식으로 오해했습니다. 정답.
rahulaga_dev

2
랜덤 생성기의 상태를 바꾸지 않습니까?
에릭 Duminil

1
난수 발생기의 상태가 충족 함수 상태 2가되는데 명시 적으로 제공하지 않는 난수를 생성하는 것은, 그 자체 부작용 인
TheInnerLight

1
rnd함수를 이스케이프하지 않으므로 상태가 변경된다는 사실은 함수의 순도에 문제가되지 않지만 Random생성자가 현재 시간을 시드 값으로 사용한다는 사실은 a및 이외의 "입력"이 있음을 의미합니다 b.
Sneftel
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.