함수를 정의하는 두 가지 조건 pure
은 다음과 같습니다.
- 부작용 없음 (즉, 로컬 범위 변경 만 허용됨)
- 동일한 입력이 주어지면 항상 동일한 출력을 반환합니다.
첫 번째 조건이 항상 참이면 두 번째 조건이 참이 아닌 경우가 있습니까?
즉, 첫 번째 조건에서만 실제로 필요합니까?
함수를 정의하는 두 가지 조건 pure
은 다음과 같습니다.
첫 번째 조건이 항상 참이면 두 번째 조건이 참이 아닌 경우가 있습니까?
즉, 첫 번째 조건에서만 실제로 필요합니까?
답변:
다음은 외부 범위를 변경하지 않지만 여전히 불순한 것으로 간주되는 몇 가지 반례입니다.
function a() { return Date.now(); }
function b() { return window.globalMutableVar; }
function c() { return document.getElementById("myInput").value; }
function d() { return Math.random(); }
(당연히 PRNG를 변경하지만 관찰 가능한 것으로 간주되지 않음)상수가 아닌 비 지역 변수에 액세스하면 두 번째 조건을 위반할 수 있습니다.
저는 항상 순도를위한 두 가지 조건을 보완 적이라고 생각합니다.
부작용 이라는 용어 는 첫 번째, 로컬이 아닌 상태를 수정하는 기능을 나타냅니다. 그러나 읽기 작업도 부작용으로 간주되기도합니다. 작업 일 때도 쓰기 작업이 포함됩니다. 주 목적이 값에 액세스하는 것이더라도 마찬가지입니다. 예를 들어 생성기의 내부 상태를 수정하는 의사 난수 생성, 읽기 위치를 전진하는 입력 스트림에서 읽기 또는 "측정 수행"명령이 포함 된 외부 센서에서 읽기가 있습니다.
prompt("you choose")
부작용이 없다면 한 발 물러서서 부작용의 의미를 명확히해야합니다.
순수한 함수 가 무엇인지 표현하는 "정상적인"방법 은 참조 투명성 측면에서입니다 . 함수가 참조 적으로 투명 하면 순수 합니다 .
참조 투명성 은 대략적으로 프로그램의 의미를 변경하지 않고 프로그램의 어느 지점에서나 함수 호출을 반환 값으로 대체하거나 그 반대로 바꿀 수 있음을 의미합니다.
예를 들어 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 투명하고, 따라서 순수. 이것은 때때로 외부 순수성 이라고 불립니다 . 즉, 외부 세계에서는 순수하게 보이지만 내부적으로는 불순한 기능입니다.
이러한 기능은 여전히 유용합니다. 왜냐하면 불순물이 주변의 모든 것을 감염시키는 반면 외부 순수 인터페이스는 일종의 "순도 장벽"을 구축하기 때문입니다. 불순물은 함수의 세 줄만 감염하지만 나머지 프로그램에는 누출되지 않습니다. . 이 세 줄은 전체 프로그램보다 정확성을 분석하기가 훨씬 쉽습니다.
memo[n]
는 멱등 적이며 읽기에 실패하면 CPU 주기만 낭비됩니다.
memo[n] = ...
먼저 사전 항목을 만든 다음 값을 저장할 수 있습니다. 그러면 다른 스레드가 초기화되지 않은 항목을 볼 수있는 창이 남습니다.
두 번째 조건은 첫 번째 조건보다 약한 제약 조건 인 것 같습니다.
예를 들어 보겠습니다. 콘솔에도 기록하는 함수를 추가하는 기능이 있다고 가정 해 보겠습니다.
function addOneAndLog(x) {
console.log(x);
return x + 1;
}
제공 한 두 번째 조건이 충족됩니다.이 함수는 동일한 입력이 주어지면 항상 동일한 출력을 반환합니다. 그러나 콘솔에 로깅하는 부작용이 있기 때문에 순수한 기능은 아닙니다.
순수 함수는 엄밀히 말하면 참조 투명성 의 속성을 만족시키는 함수입니다 . 그것이 우리가 프로그램의 동작을 변경하지 않고 생성하는 값으로 함수 애플리케이션을 대체 할 수있는 속성입니다.
단순히 다음을 추가하는 함수가 있다고 가정합니다.
function addOne(x) {
return x + 1;
}
우리는 대체 할 수 addOne(5)
와 6
우리의 프로그램 변경됩니다 아무것도 어디서나.
대조적으로, 첫 번째 표현식은 콘솔에 어떤 내용이 기록되는 반면 두 번째 표현식은 그렇지 않기 때문에 동작을 변경하지 않고 프로그램의 어느 곳에서나 addOneAndLog(x)
값으로 바꿀 수 6
없습니다.
addOneAndLog(x)
출력을 반환 하는 것 외에 수행 되는 이러한 추가 동작을 부작용으로 간주합니다 .
Date.now()
순수하거나 참조 적으로 투명하지는 않지만 부작용이 있기 때문이 아니라 그 결과가 입력 이상의 것에 의존하기 때문에 당신은 완전히 옳 습니다.
시스템 외부에서 임의의 소스가있을 수 있습니다. 계산의 일부에 실내 온도가 포함되어 있다고 가정합니다. 그런 다음 함수를 실행하면 실내 온도의 임의의 외부 요소에 따라 매번 다른 결과가 생성됩니다. 프로그램을 실행해도 상태는 변경되지 않습니다.
어쨌든 내가 생각할 수있는 모든 것.
FP 정의의 문제점은 매우 인위적이라는 것입니다. 각 평가 / 계산은 평가자에게 부작용이 있습니다. 이론적으로는 사실입니다. 이것에 대한 부정은 FP 사과가 철학과 논리를 무시한다는 것을 보여줍니다. "평가"는 어떤 지능적인 환경 (기계, 뇌 등)의 상태를 변화시키는 것을 의미합니다. 이것이 평가 프로세스의 특성입니다. 변경 없음- "미적분"없음. 그 효과는 매우 눈에 띄게 나타날 수 있습니다. CPU 가열 또는 고장, 과열시 마더 보드 종료 등입니다.
참조 투명성에 대해 이야기 할 때 이러한 투명성에 대한 정보는 전체 시스템의 작성자이자 의미 정보의 소유자로서 인간이 사용할 수 있으며 컴파일러에서는 사용할 수 없음을 이해해야합니다. 예를 들어 함수는 일부 외부 리소스를 읽을 수 있으며 서명에 IO 모나드가 있지만 항상 동일한 값을 반환합니다 (예 :의 결과 current_year > 0
). 컴파일러는 함수가 항상 동일한 결과를 반환한다는 것을 알지 못하므로 함수는 불순하지만 참조 적으로 투명한 속성을 True
가지며 상수 로 대체 될 수 있습니다 .
따라서 이러한 부정확성을 방지하려면 프로그래밍 언어에서 수학 함수와 "함수"를 구분해야합니다. Haskell의 함수는 항상 불순하고 그와 관련된 순도의 정의는 항상 매우 조건부입니다. 실제 하드웨어에서 실행되고 있으며 실제 부작용과 물리적 특성은 수학적 함수에 적합하지 않습니다. 이것은 "printf"함수가있는 예제가 완전히 잘못되었음을 의미합니다.
그러나 모든 수학 함수도 순수하지는 않습니다 t
. 매개 변수로 (시간) 을 갖는 각 함수는 불순 할 수 있습니다. t
함수의 모든 효과와 확률 적 특성을 보유합니다. 일반적으로 입력 신호가 있고 실제 값에 대해 알지 못합니다. 소음 일 수도 있습니다.
첫 번째 조건이 항상 참이면 두 번째 조건이 참이 아닌 경우가 있습니까?
예
아래의 간단한 코드 스 니펫을 고려하십시오.
public int Sum(int a, int b) {
Random rnd = new Random();
return rnd.Next(1, 10);
}
이 코드는 동일한 입력 세트에 대해 임의의 출력을 반환하지만 부작용은 없습니다.
당신이 언급 한 포인트 # 1과 # 2의 전체적인 효과는 다음을 의미합니다 : 같은 i / p를 가진 기능 Sum
이 프로그램에서 그 결과로 대체된다면, 프로그램의 전체적인 의미는 변하지 않습니다 . 이것은 참조 투명성 일뿐 입니다.
rnd
함수를 이스케이프하지 않으므로 상태가 변경된다는 사실은 함수의 순도에 문제가되지 않지만 Random
생성자가 현재 시간을 시드 값으로 사용한다는 사실은 a
및 이외의 "입력"이 있음을 의미합니다 b
.