동일한 입력이 항상 동일한 출력을 반환하지만 부작용이있는 함수를 무엇이라고 부릅니까?


43

다음과 같은 정상적인 순수한 기능이 있다고 가정 해보십시오.

function add(a, b) {
  return a + b
}

그런 다음 부작용을 갖도록 변경합니다.

function add(a, b) {
  writeToDatabase(Math.random())
  return a + b;
}

사람들이 순수한 기능을 "부작용없는 기능"이라고 부르는 것을 종종 듣기 때문에 내가 아는 한 순수한 기능으로 간주되지 않습니다. 그러나 동일한 입력에 대해 동일한 출력을 반환한다는 점에서 순수 함수처럼 작동합니다.

이 유형의 함수에 다른 이름이 있습니까, 이름이 없거나, 여전히 실제로 순수하고 순도의 정의에 대해 잘못 알고 있습니까?


91
"순수한 기능이 아닙니다".
로스 패터슨

2
@RossPatterson 그것은 내가 생각한 것이기도하지만, 참조 투명성에 대해 배웠으므로 스스로 그것을 지키지 않아서 기쁘다.
m0meni

9
경우 writeToDatabase는 두 번째 만들기 때문에 예외를 게재 할 수 실패 add... 전에 문제가 발생하지 않았다 같은 인수로 호출하는 경우에도 예외 때때로 함수 농산물을 대부분의 시간은 부작용을 소개합니다 오류 관련 상황이 이런 종류의 휴식 데 "입력-출력 순도".
Bakuriu

25
주어진 입력에 대해 항상 동일한 출력을 제공하는 것을 결정 론적 이라고 합니다.
njzk2

2
@ njzk2 : 맞습니다. 또한 상태가 없습니다 . 상태 결정적 함수가 모든 입력에 대해 동일한 출력을주지 않을 수도있다. 예 : 이전 호출과 동일한 인수로 호출 된 경우 F(x)반환되도록 정의됩니다 true. 분명히 시퀀스를 사용 {1,2,2} => {undefined, false, true}하면 결정 론적이지만에 대해 다른 출력을 제공합니다 F(2).
MSalters

답변:


85

순도에 대한 보편적 인 정의는 확실하지 않지만 Haskell (프로그래머가 순도 및 참조 투명성과 같은 것들에 관심을 갖는 언어)의 관점에서 볼 때 함수의 첫 번째 함수는 "순수"입니다. 의 두 번째 버전은 add순수하지 않습니다 . 따라서 귀하의 질문에 대한 답변으로 "불완전"이라고 부릅니다.

이 정의에 따르면 순수한 함수는 다음과 같은 함수입니다.

  1. 입력에만 의존합니다. 즉, 동일한 입력이 주어지면 항상 동일한 출력을 반환합니다.
  2. 참조 투명성 : 기능을 값으로 자유롭게 대체 할 수 있으며 프로그램의 "동작"은 변경되지 않습니다.

이 정의를 사용하면 규칙 2를 위반하므로 두 번째 함수를 순수하게 간주 할 수 없습니다. 즉, 다음 두 프로그램은 동일하지 않습니다.

function f(a, b) { 
    return add(a, b) + add(a, b);
}

function g(a, b) {
    c = add(a, b);
    return c + c;
}

두 함수가 모두 같은 값을 반환하더라도 함수는 f데이터베이스에 두 번 쓰지만 g한 번 쓰게됩니다! 데이터베이스에 대한 쓰기가 프로그램의 관찰 가능한 동작의 일부일 가능성이 큽니다.이 경우 두 번째 버전의 add"순수함"이 아닙니다.

데이터베이스 쓰기가 프로그램 동작에서 관찰 가능한 부분이 아닌 경우 두 버전 모두 add동일하고 순수한 것으로 간주 될 수 있습니다. 그러나 데이터베이스에 쓰는 것이 중요하지 않은 시나리오는 생각할 수 없습니다. 로깅도 중요합니다!


1
참조 투명성이 주어지면 "입력에만 의존"되지 않습니까? RT가 순도와 동의어를 의미하는 것은 무엇입니까? (내가
찾은

혼란 스럽습니다. 나는 생각한 예만 생각할 수있다. Say f(x)x외부 글로벌 변수에 의존 할 뿐만 아니라 일부 변수에 의존 합니다 y. 그런 다음 fRT 속성이 있으면를 터치하지 않는 한 모든 발생을 반환 값 으로 자유롭게 바꿀 수 있습니다 y. 예, 내 예는 모호합니다. 그러나 중요한 것은 f데이터베이스에 쓰거나 로그에 쓰면 RT의 속성을 잃어 버린다는 것입니다. 이제 전역을 y건드리지 않든 상관없이 실제로 의미에 따라 프로그램의 의미가 바뀌는 것을 수 있습니다 호출 f하거나 단순히 반환 값을 사용하십시오.
Andres F.

험프. 부작용을 제외하고 순수한 기능을 가지고 있으며 두 샘플이 동등한 경우에도 이와 같은 동작을 보장합니다. (실제로이 사례가 나오게되었으므로 가설은 아닙니다.) 우리는 아직 완료되지 않았다고 생각합니다.
여호수아

2
나는 두 번째 함수가 규칙 # 1을 어길 수 있다고 주장합니다. 사용중인 데이터베이스 API의 언어 및 오류 처리 관행에 따라 데이터베이스를 사용할 수 없거나 어떤 이유로 인해 DB 쓰기에 실패하면 함수가 아무 것도 반환하지 않을 수 있습니다.
Zach Lipton

1
Haskell이 언급 된 이후 : Haskell에서 이와 같은 부작용을 추가하려면 함수의 서명을 변경 해야 합니다 . (원래 데이터베이스를 추가 입력으로 제공하고 수정 된 데이터베이스를 함수의 추가 반환 값으로 얻는 것과 같이 생각하십시오). 실제로 유형 시스템에서 부작용을 매우 우아하게 모델링하는 것이 가능합니다. 오늘날의 주류 언어는 부작용과 순수성을 충분히 신경 쓰지 않습니다.
ComicSansMS

19

동일한 입력이 항상 동일한 출력을 반환하지만 부작용이있는 함수를 무엇이라고 부릅니까?

이러한 기능을

결정 론적

입력에서 동작을 완전히 예측할 수있는 알고리즘입니다.

termwiki.com

상태에 관하여 :

사용하는 함수의 정의에 따라 함수에는 상태가 없습니다. 객체 지향 세계에서 온다면 이것이 x.f(y)방법 이라는 것을 기억하십시오 . 함수로서 다음과 같습니다 f(x,y). 어휘 범위가 동봉 된 클로저를 사용하는 경우에는 불변 상태가 함수 표현식의 일부일 수도 있습니다. 함수 결정 론적 특성에 영향을 미치는 것은 단지 변경 가능한 상태입니다. 따라서 f (x) = x + 1은 1이 변하지 않는 한 결정적입니다. 1이 저장된 위치는 중요하지 않습니다.

당신의 기능은 결정 론적입니다. 첫 번째는 순수한 기능입니다. 두 번째는 순수하지 않습니다.

순수한 기능

  1. 함수는 항상 동일한 인수 값이 주어지면 동일한 결과 값을 평가합니다. 기능 결과 값은 프로그램 실행이 진행되는 동안 또는 프로그램의 다른 실행간에 변경 될 수있는 숨겨진 정보 나 상태에 의존 할 수 없으며 I / O 장치의 외부 입력에 의존 할 수도 없습니다.

  2. 결과의 평가는 변경 가능한 객체의 돌연변이 또는 I / O 장치로의 출력과 같이 의미 적으로 관찰 가능한 부작용 또는 출력을 유발하지 않습니다.

wikipedia.org

포인트 1은 결정적 임을 의미 합니다. 포인트 2는 참조 투명성을 의미 합니다. 둘 다 순수한 함수는 인수와 반환 값만 변경할 수 있음을 의미합니다. 다른 변화는 없습니다. 다른 것은 바뀌지 않았습니다.


-1. 데이터베이스에 쓰는 것은 일반적으로 입력을보고 결정할 수없는 외부 상태에 따라 다릅니다. 여러 가지 이유로 데이터베이스를 사용할 수 없으며 작업의 성공 여부를 예측할 수 없습니다. 이것은 결정론적인 행동이 아닙니다.
Frax

@Frax 시스템 메모리를 사용하지 못할 수 있습니다. CPU를 사용할 수 없습니다. 결정적이라는 것이 성공을 보장하지는 않습니다. 성공적인 행동을 예측할 수 있습니다.
candied_orange

OOMing은 특정 기능에 국한되지 않으며 문제의 다른 범주입니다. 이제 "순수 함수"정의의 1 점 (실제로 "결정적"을 의미 함)을 살펴 보겠습니다. "함수 결과 값은 프로그램 실행이 진행되는 동안 또는 서로 다른 실행간에 변경 될 수 있는 숨겨진 정보 나 상태 에 의존 할 수 없습니다. 프로그램 이나 I / O 장치의 외부 입력에 의존 할 수 없습니다. " 데이터베이스는 그러한 종류의 상태이므로 OP 기능은 분명히이 조건을 충족하지 못합니다. 결정적이지 않습니다.
Frax

@candied_orange DB 쓰기가 입력에만 의존하는 경우 동의합니다. 그러나 그것은 Math.random()입니다. 따라서 실제 RNG 대신 PRNG를 가정하고 PRNG가 입력의 일부 (참조가 하드 코딩되지 않음)를 나타내는 것으로 간주하지 않는 한 결정적이지 않습니다.
marstato '11

1
@candied_orange 결정적 상태 인용 " 입력에서 행동 을 완전히 예측할 수 있는 알고리즘 " 나에게 IO에 쓰는 것은 결과가 아니라 행동입니다.
marstato

9

부작용에 신경 쓰지 않으면 참조 적으로 투명합니다. 물론 당신은 신경 쓰지 않고 다른 사람이 할 수 있으므로 용어의 적용 가능성은 상황에 따라 다릅니다.

정확히 설명하는 속성에 대한 일반적인 용어는 알지 못하지만 중요한 부분 집합은 dem 등원 입니다. 수학과는 약간 다른 컴퓨터 과학에서 dem 등식 기능 은 동일한 효과로 반복 될 수 있습니다. 다시 말해서, 여러 번 수행 한 nett 부작용 결과는 한 번 수행하는 것과 같습니다.

따라서 부작용으로 특정 행의 특정 값으로 데이터베이스를 업데이트하거나 정확히 일관된 내용으로 파일을 작성하는 경우에는 dem 등원 이지만 데이터베이스에 추가되거나 파일에 추가되면 , 그렇지 않을 것입니다.

dem 등원 기능의 조합은 전체적으로 dem 등원 일 수도 있고 아닐 수도 있습니다.

* 컴퓨터 과학에서 수학과 다르게 dem 등원을 사용하는 것은 그 개념이 유용하기 때문에 수학 용어를 잘못 사용하여 나온 것으로 보인다.


3
"보통 적으로 투명한"이라는 용어는 "누구의 관심"여부보다 더 엄격하게 정의됩니다. 우리는 등 연결 문제 누락 된 연결 문자열, 시간 제한 등의 IO 문제를 무시하더라도, 그것은 프로그램을 대체한다는 것을 보여주기 위해 쉬운 아직 (f x, f x)와이 let y = f x in (y, y)배 빠른 당신이이 주장 할 수 디스크 공간 예외 아웃으로 실행됩니다 당신이 신경 쓰지 않는 엣지 케이스이지만, 애매한 정의로 우리는 new Random().Next()참조 투명하게 호출 할 수 있습니다 .
SARA

@kai : 상황에 따라 부작용을 무시할 수 있습니다. 반면에 random과 같은 함수의 반환 값은 부작용이 아닙니다. 주요 효과입니다.
조르지오

Random.Next.NET의 @Giorgio 는 실제로 부작용이 있습니다. 매우 그렇다. 가능하면 Next변수에 변수를 할당 한 다음 Next다시 호출 하여 다른 변수에 할당하면 변수가 같지 않을 가능성이 있습니다. 왜? 호출 Next하면 Random오브젝트 에서 일부 숨겨진 내부 상태가 변경 되기 때문 입니다. 이것은 참조 투명성과 정반대입니다. "주요 효과"가 부작용이 될 수 없다는 귀하의 주장을 이해하지 못합니다. 명령형 코드에서는 명령형 프로그램이 본질적으로 상태가 양호하기 때문에 주 효과가 부작용이 아닌 것보다 흔합니다.
sara

3

나는 그러한 함수가 어떻게 호출되는지 (또는 일부 체계적인 이름이 있는지) 알지 못하지만 순수한 (다른 답변이 울퉁불퉁 한 것처럼) 함수는 아니지만 같은 매개 변수를 제공하면 항상 동일한 결과를 반환합니다. parameters "(매개 변수 및 다른 상태의 기능과 비교). 나는 그것을 단지 함수라고 부를 수 있지만, 불행히도 우리가 프로그래밍의 맥락에서 "기능"이라고 말할 때, 우리는 실제 기능이 될 필요가없는 것을 의미합니다.


1
동의했다! 그것은 "비공식적으로" "함수"의 수학적 정의이지만, 불행히도 "함수"는 "값을 얻는 데 필요한 단계별 절차"에 더 가까운 프로그래밍 언어에서 다른 것을 의미합니다.
Andres F.

2

기본적으로 불순물에 대한 관심 여부에 따라 다릅니다. 이 테이블의 의미가 몇 개의 항목이 있는지 신경 쓰지 않으면 순수한 것입니다. 그렇지 않으면 순수하지 않습니다.

또는 달리 말하면, 순도를 기반으로 한 최적화가 프로그램 의미를 손상시키지 않는 한 괜찮습니다.

보다 현실적인 예는이 함수를 디버깅하려고하고 로깅 명령문을 추가 한 경우입니다. 기술적으로 로깅은 부작용입니다. 로그가 불완전합니까? 아니.


글쎄요. 예를 들어, 로그에 "INFO f () called"가 몇 번이나 언제 나타나는지에 대해 로그가 불완전 할 수 있습니다. 어떤 경우가 많습니다 :) 할
안드레스 F.

8
-1 로그가 중요합니다. 대부분의 플랫폼에서 모든 종류의 출력은 암시 적으로 실행 스레드를 동기화합니다. 프로그램 동작은 다른 디스크 쓰기, 외부 로그 기록기, 때로는 로그 읽기, 파일 디스크립터 상태에 따라 달라집니다. 먼지통만큼이나 순수합니다.
Basilevs

@AndresF. 글쎄, 당신은 아마 문자적인 횟수에 신경 쓰지 않을 것입니다. 함수가 호출 된 횟수만큼만 기록되도록 신경 쓰실 것입니다.
DeadMG

@Basilevs 함수의 동작은 전혀 의존하지 않습니다. 로그 쓰기가 실패하면 계속 진행하십시오.
DeadMG

2
로거를 실행 환경의 일부로 정의할지 여부를 결정해야합니다. 다른 예를 들어, 프로세스에 디버거를 연결하고 중단 점을 설정하면 순수한 함수가 여전히 순수한가요? 디버거를 사용하는 사람의 POV에서 분명히 함수에 부작용이 있지만 일반적으로 우리는 이것이 "카운트하지 않는다"는 규칙으로 프로그램을 분석합니다. 디버깅에 사용되는 로깅에도 동일한 캔을 사용할 수 있지만 반드시 그래야 할 필요는 없습니다. 트레이스가 불순물을 숨기고있는 이유입니다. 미션 크리티컬 로깅, 예를 들어, 감사에 대한 물론이 있다 상당한 부작용.
Steve Jessop

1

가장 좋은 방법은 전화하는 방법이 아니라 그러한 코드를 분석 하는 방법 입니다. 그리고 그러한 분석에서 나의 첫 번째 주요 질문은 다음과 같습니다.

  • 부작용은 함수의 인수 또는 부작용의 결과에 의존합니까?
    • 아니요 : "효과적인 기능"은 순수한 기능, 효과적인 행동 및 이들을 결합하는 메커니즘으로 리팩토링 될 수 있습니다.
    • 예 : "유효 기능"은 모나드 결과를 생성하는 기능입니다.

이것은 Haskell에서 간단하게 설명 할 수 있습니다. "아니오"사례의 예는 다음과 같습니다.

double :: Num a => a -> IO a
double x = do
  putStrLn "I'm doubling some number"
  return (x*2)

이 예에서 우리가 취하는 조치 (행 인쇄 "I'm doubling some number")는 x결과와의 관계에 영향을 미치지 않습니다 . 이것은 Applicative클래스와 *>연산자를 사용하여 이런 식으로 리팩토링 할 수 있다는 것을 의미 합니다. 이는 함수와 효과가 실제로 직교 함을 보여줍니다.

double :: Num a => a -> IO a
double x = action *> pure (function x)
  where
    -- The pure function 
    function x = x*2  
    -- The side effect
    action = putStrLn "I'm doubling some number"

따라서이 경우 개인적으로 순수한 기능을 제외시킬 수있는 경우라고 말합니다. 많은 Haskell 프로그래밍이 이것에 관한 것입니다. 효과적인 코드에서 순수한 부분을 제거하는 방법을 배우십시오.

순수하고 효과적인 부분이 직교하지 않는 "예"정렬의 예 :

double :: Num a => a -> IO a
double x = do
  putStrLn ("I'm doubling the number " ++ show x)
  return (x*2)

이제 인쇄하는 문자열은의 값에 따라 다릅니다 x. 그러나 함수 부분 ( x2를 곱한 값 )은 효과에 전혀 의존하지 않으므로 여전히 요소를 제외시킬 수 있습니다.

logged :: (a -> b) -> (a -> IO x) -> IO b
logged function logger a = do
  logger a
  return (function a)

double x = logged function logger
  where function = (*2) 
        logger x putStrLn ("I'm doubling the number " ++ show x)

나는 다른 예제들을 계속 설명 할 수는 있지만, 이것이 내가 시작한 요점을 설명하기에 충분하기를 바랍니다. 당신은 무언가를 "부르지"않고 순수하고 효과적인 부분들이 어떻게 관련되어 있는지를 분석하고 그것을 고려하지 않습니다. 당신의 이점에.

이것이 Haskell이 Monad클래스를 광범위하게 사용하는 이유 중 하나입니다 . 모나드는 이런 종류의 분석 및 리팩토링을 수행하는 도구입니다.


-2

부작용을 일으키는 기능을 종종 효과라고 합니다 . 예 https://slpopejoy.github.io/posts/Effectful01.html


널리 인정되는 용어를 언급하는 것만 대답하면 투표가 중단됩니다 .. 무지가 행복하다고 생각합니다. ..
벤 허치슨

"효과적인"은 해당 게시물의 작성자가 "부작용이있는 것"을 의미하기로 선택한 단어입니다. 그는 그렇게 말합니다.
Robert Harvey

인터넷 검색의 효과적인 기능 은 널리 사용되는 용어로 충분한 증거를 보여줍니다. 블로그 게시물은 정의가 아니라 많은 예제 중 하나로 제공되었습니다. 순수한 함수가 기본 인 함수형 프로그래밍 환경에서는 의도적으로 부작용을 나타내는 함수를 설명하는 긍정적 인 용어가 필요합니다. 즉 순도부재 그 이상 입니다. 그 용어가 효과적입니다. 자, 당신은 교육받은 것을 고려하십시오.
벤 허치슨

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