[programming-languages] 절차와 기능의 차이를 진정으로 이해

절차 적 프로그래밍 패러다임 과 함수 프로그래밍 패러다임 의 차이점을 이해하는 데 정말 어려움을 겪고 있습니다.

다음은 함수형 프로그래밍 에 대한 Wikipedia 항목의 처음 두 단락입니다 .

컴퓨터 과학에서 함수형 프로그래밍은 계산을 수학적 함수의 평가로 취급하고 상태 및 변경 가능한 데이터를 피하는 프로그래밍 패러다임입니다. 상태의 변화를 강조하는 명령형 프로그래밍 스타일과 달리 함수의 적용을 강조합니다. 함수형 프로그래밍은 함수 정의, 함수 적용 및 재귀를 조사하기 위해 1930 년대에 개발 된 공식 시스템 인 람다 미적분에 뿌리를두고 있습니다. 많은 함수형 프로그래밍 언어는 람다 미적분에 대한 정교함으로 볼 수 있습니다.

실제로 수학적 함수와 명령형 프로그래밍에서 사용되는 “함수”개념의 차이점은 명령형 함수는 프로그램 상태의 값을 변경하는 부작용을 가질 수 있다는 것입니다. 이 때문에 참조 투명성이 부족합니다. 즉, 동일한 언어 표현이 실행중인 프로그램의 상태에 따라 다른 시간에 다른 값을 생성 할 수 있습니다. 반대로 함수 코드에서 함수의 출력 값은 함수에 입력 된 인수에만 의존하므로 인수 f에 대해 동일한 값 으로 함수를 두 번 호출
x하면 동일한 결과가 생성됩니다.f(x)두 번. 부작용을 제거하면 프로그램의 동작을 훨씬 쉽게 이해하고 예측할 수 있으며, 이는 함수형 프로그래밍 개발의 핵심 동기 중 하나입니다.

2 항에서

반대로 함수 코드에서 함수의 출력 값은 함수에 입력 된 인수에만 의존하므로 인수 f에 대해 동일한 값 으로 함수를 두 번 호출 x하면 f(x)두 번 모두 동일한 결과가 생성됩니다 .

절차 적 프로그래밍의 경우와 똑같지 않습니까?

눈에 띄는 절차 적 대 기능적 측면에서 무엇을 찾아야합니까?



답변

함수형 프로그래밍

함수형 프로그래밍은 함수를 값으로 처리하는 능력을 나타냅니다.

“일반”값과의 비유를 고려해 봅시다. 두 개의 정수 값을 가져 와서 +새 정수를 얻기 위해 연산자를 사용하여 결합 할 수 있습니다 . 또는 정수에 부동 소수점 수를 곱하여 부동 소수점 수를 얻을 수 있습니다.

함수형 프로그래밍에서는 compose 또는 lift 같은 연산자를 사용하여 두 함수 값을 결합하여 새로운 함수 값을 생성 할 수 있습니다 . 또는 함수 값과 데이터 값을 결합하여 map 또는 fold 와 같은 연산자를 사용하여 새로운 데이터 값을 생성 할 수 있습니다 .

많은 언어에는 함수형 프로그래밍 기능이 있습니다. 일반적으로 함수형 언어로 간주되지 않는 언어도 있습니다. Grandfather FORTRAN조차도 함수 값을 지원했지만 함수 결합 연산자 방식에서는 많이 제공하지 않았습니다. 언어를 “기능적”이라고 부르려면 함수형 프로그래밍 기능을 크게 수용해야합니다.

절차 적 프로그래밍

절차 적 프로그래밍은 복사 및 붙여 넣기에 의존하지 않고 여러 위치에서 이러한 명령어를 호출 할 수 있도록 일반적인 명령어 시퀀스를 프로 시저로 캡슐화하는 기능을 말합니다. 프로시 저는 프로그래밍에서 매우 초기 개발 이었기 때문에이 기능은 기계 또는 어셈블리 언어 프로그래밍에서 요구하는 프로그래밍 스타일과 거의 항상 연결되어 있습니다.이 스타일은 저장 위치 및 해당 위치간에 데이터를 이동하는 명령의 개념을 강조하는 스타일입니다.

대조

두 스타일은 정반대가 아닙니다. 서로 다를뿐입니다. 두 스타일을 완전히 포용하는 언어가 있습니다 (예 : LISP). 다음 시나리오는 두 스타일의 차이점을 보여줄 수 있습니다. 목록의 모든 단어에 홀수 문자가 있는지 확인하려는 넌센스 요구 사항에 대한 코드를 작성해 보겠습니다. 첫째, 절차 적 스타일 :

function allOdd(words) {
  var result = true;
  for (var i = 0; i < length(words); ++i) {
    var len = length(words[i]);
    if (!odd(len)) {
      result = false;
      break;
    }
  }
  return result;
}

이 예는 이해할 수 있다는 점을 감안할 것입니다. 이제 기능적인 스타일 :

function allOdd(words) {
  return apply(and, map(compose(odd, length), words));
}

이 정의는 내부에서 밖으로 작동하여 다음 작업을 수행합니다.

  1. compose(odd, length)oddlength함수를 결합하여 문자열 길이가 홀수인지 여부를 결정하는 새 함수를 생성합니다.
  2. map(..., words)의 각 요소에 대해 새 함수를 호출하여 words궁극적으로 각각 해당 단어에 홀수 문자가 있는지 여부를 나타내는 새로운 부울 값 목록을 반환합니다.
  3. apply(and, ...)결과 목록에 “및”연산자를 적용 하고 , 최종 결과를 생성하도록 함께 부울 모두 -ing.

이 예제에서 절차 적 프로그래밍은 변수에서 값을 이동하고 최종 결과를 생성하는 데 필요한 작업을 명시 적으로 설명하는 것과 매우 관련이 있음을 알 수 있습니다. 대조적으로, 기능 스타일은 초기 입력을 최종 출력으로 변환하는 데 필요한 기능 조합을 강조합니다.

이 예제는 또한 절차 적 코드와 기능적 코드의 일반적인 상대적 크기를 보여줍니다. 또한 절차 적 코드의 성능 특성이 기능적 코드의 성능 특성보다 더 쉽게 볼 수 있음을 보여줍니다. 고려 사항 : 함수가 목록에있는 모든 단어의 길이를 계산합니까, 아니면 첫 번째 짝수 길이 단어를 찾은 후 즉시 중지합니까? 반면에 기능 코드는 명시적인 알고리즘이 아닌 의도를 주로 표현하기 때문에 고품질 구현이 꽤 심각한 최적화를 수행하도록 허용합니다.

추가 읽기

이 질문은 많이 나옵니다. 예를 들면 다음과 같습니다.

John Backus의 Turing Award 강의는 함수형 프로그래밍의 동기를 매우 자세하게 설명합니다.

프로그래밍이 폰 노이만 스타일에서 해방 될 수 있습니까?

저는 현재의 맥락에서 그 논문을 언급하지 말아야합니다. 왜냐하면 그것은 상당히 기술적이고 매우 빠르게되기 때문입니다. 정말 기초라고 생각하기 때문에 저항 할 수 없었습니다.


부록-2013

해설자들은 인기있는 현대 언어가 절차 적 및 기능적 이상의 다른 프로그래밍 스타일을 제공한다고 지적합니다. 이러한 언어는 종종 다음 프로그래밍 스타일 중 하나 이상을 제공합니다.

  • 쿼리 (예 : 목록 이해, 언어 통합 쿼리)
  • 데이터 흐름 (예 : 암시 적 반복, 대량 작업)
  • 객체 지향 (예 : 캡슐화 된 데이터 및 메서드)
  • 언어 지향 (예 : 응용 프로그램 별 구문, 매크로)

이 응답의 의사 코드 예제가 이러한 다른 스타일에서 사용할 수있는 일부 기능에서 이점을 얻을 수있는 방법에 대한 예제는 아래 설명을 참조하십시오. 특히, 절차 적 예는 거의 모든 상위 레벨 구성의 적용에서 이점을 얻을 수 있습니다.

표시된 예제는 논의중인 두 스타일 간의 차이를 강조하기 위해 이러한 다른 프로그래밍 스타일에서 혼합을 의도적으로 피합니다.


답변

함수형 프로그래밍과 명령형 프로그래밍의 실제 차이점은 사고 방식입니다. 명령형 프로그래머는 변수와 메모리 블록을 생각하는 반면, 함수형 프로그래머는 ” 입력 데이터를 출력 데이터로 변환 하는 방법”을 생각 합니다. “프로그램”은 파이프 라인입니다. 입력에서 출력으로 가져 오기 위해 데이터 에 대한 변환 세트 . 그것은 “변수를 사용하지 말라”비트가 아니라 IMO에서 흥미로운 부분입니다.

이 사고 방식의 결과로, FP 프로그램은 일반적으로 설명하는 대신에 특정 메커니즘, 일어날 방법 이 일어날 것입니다 – 우리는 분명히 “어디”와 “집계”라는 뜻을 무엇을 “선택”을 명시 할 수 있다면 때문에 강력한, 우리를 AsParallel ()과 마찬가지로 구현을 자유롭게 교체 할 수 있으며 갑자기 단일 스레드 앱이 n 코어로 확장됩니다 .


답변

     Isn't that the same exact case for procedural programming?

아니요, 절차 코드에는 부작용이있을 수 있기 때문입니다. 예를 들어, 호출 사이에 상태를 저장할 수 있습니다.

즉, 절차 적 언어로이 제약 조건을 충족하는 코드를 작성할 수 있습니다. 또한 기능적으로 간주되는 일부 언어에서이 제약 조건을 위반하는 코드를 작성할 수도 있습니다.


답변

WReach의 대답에 동의하지 않습니다. 불일치가 어디에서 왔는지 확인하기 위해 그의 대답을 조금 분해 해 보겠습니다.

첫째, 그의 코드 :

function allOdd(words) {
  var result = true;
  for (var i = 0; i < length(words); ++i) {
    var len = length(words[i]);
    if (!odd(len)) {
      result = false;
      break;
    }
  }
  return result;
}

function allOdd(words) {
  return apply(and, map(compose(odd, length), words));
}

가장 먼저 주목해야 할 것은 그가 합병하고 있다는 것입니다.

  • 기능의
  • 표현 지향적이고
  • 반복자 중심

반복적 스타일 프로그래밍이 일반적인 기능적 스타일보다 더 명시적인 제어 흐름을 가질 수있는 능력이 없습니다.

이것에 대해 빨리 이야기합시다.

표현 중심 스타일은 가능한 한 사물 을 사물로 평가 하는 스타일입니다 . 함수형 언어는 표현에 대한 사랑으로 유명하지만 실제로는 조합 가능한 표현 없이도 함수형 언어를 가질 수 있습니다. 표현 이 없는 문장 만 만들어 보겠습니다 .

lengths: map words length
each_odd: map lengths odd
all_odd: reduce each_odd and

이것은 함수가 명령문과 바인딩의 체인을 통해 순전히 연결된다는 점을 제외하면 이전에 제공된 것과 거의 동일합니다.

반복자 중심의 프로그래밍 스타일은 Python이 취하는 스타일 일 수 있습니다. 순전히 반복적 인 반복자 중심 스타일을 사용하겠습니다 .

def all_odd(words):
    lengths = (len(word) for word in words)
    each_odd = (odd(length) for length in lengths)
    return all(each_odd)

각 절은 반복적 인 프로세스이고 스택 프레임의 명시 적 일시 중지 및 재개에 의해 함께 묶여 있기 때문에 이것은 작동하지 않습니다. 구문은 기능적 언어에서 부분적으로 영감을받을 수 있지만 완전히 반복적 인 구현에 적용됩니다.

물론 이것을 압축 할 수 있습니다.

def all_odd(words):
    return all(odd(len(word)) for word in words)

명령형은 지금 그렇게 나쁘게 보이지 않습니까? 🙂

마지막 요점은보다 명확한 제어 흐름에 관한 것입니다. 이것을 사용하기 위해 원래 코드를 다시 작성해 보겠습니다.

function allOdd(words) {
    for (var i = 0; i < length(words); ++i) {
        if (!odd(length(words[i]))) {
            return false;
        }
    }
    return true;
}

반복기를 사용하면 다음을 수행 할 수 있습니다.

function allOdd(words) {
    for (word : words) { if (!odd(length(word))) { return false; } }
    return true;
}

그래서 입니다 차이가 사이 인 경우 함수형 언어의 요점은 :

return all(odd(len(word)) for word in words)
return apply(and, map(compose(odd, length), words))
for (word : words) { if (!odd(length(word))) { return false; } }
return true;

함수형 프로그래밍 언어의 주요 특징은 일반적인 프로그래밍 모델의 일부로 변이를 제거한다는 것입니다. 사람들은 종종 이것을 함수형 프로그래밍 언어가 명령문이 없거나 표현식을 사용하지 않는다는 것을 의미하지만 이것은 단순화입니다. 기능적 언어는 명시 적 계산을 행동 선언으로 대체 한 다음 언어가 감소를 수행합니다.

이 기능의 하위 집합으로 자신을 제한하면 프로그램의 동작에 대해 더 많은 보증을받을 수 있으며이를 통해보다 자유롭게 구성 할 수 있습니다.

기능적 언어가있는 경우 새 함수를 만드는 것은 일반적으로 밀접하게 관련된 함수를 구성하는 것만 큼 간단합니다.

all = partial(apply, and)

함수의 전역 종속성을 명시 적으로 제어하지 않은 경우 간단하지 않거나 가능하지 않을 수도 있습니다. 함수형 프로그래밍의 가장 큰 특징은보다 일반적인 추상화를 지속적으로 생성 할 수 있고 더 큰 전체로 결합 될 수 있다는 신뢰입니다.


답변

절차 적 패러다임 (대신 “구조화 된 프로그래밍”이라고 말해야합니까?)에서, 당신은 가변 메모리와이를 어떤 순서로 읽고 / 쓰기하는 명령어를 공유했습니다.

기능적 패러다임에는 변수와 함수가 있습니다 (수학적 의미에서 : 변수는 시간이 지남에 따라 변하지 않으며 함수는 입력에 따라 무언가를 계산할 수 있습니다).

(이는 지나치게 단순화되어 있습니다. 예를 들어 FPL은 일반적으로 가변 메모리 작업을위한 기능을 가지고있는 반면, 절차 적 언어는 종종 고차 절차를 지원할 수 있으므로 일이 명확하지는 않지만 아이디어를 얻을 수 있습니다.)


답변

매력적인 파이썬 : 파이썬에서 함수 프로그래밍 에서 한국 IBM developerWorks는 정말 차이를 이해하는 것이 나에게 도움을 주었다.

특히 Python을 조금 아는 사람에게는 기능적 및 절차 적으로 서로 다른 작업을 수행하는이 기사의 코드 예제를 통해 절차 적 프로그래밍과 함수형 프로그래밍의 차이점을 명확히 할 수 있습니다.


답변

기호 (변수 또는 함수 이름)의 의미를 추론하기 위해 함수형 프로그래밍에서 현재 범위와 기호 이름의 두 가지만 알면됩니다. 불변성을 가진 순전히 기능적인 언어를 가지고 있다면이 두 가지 모두 “정적”(심하게 오버로드 된 이름에 대해 죄송합니다) 개념입니다. 즉, 소스 코드를 보면 현재 범위와 이름을 모두 볼 수 있습니다.

절차 적 프로그래밍에서이면에있는 가치가 무엇인지에 대한 질문에 답하려면 x거기에 어떻게 도달했는지 알아야합니다. 범위와 이름만으로는 충분하지 않습니다. 이 실행 경로는 “런타임”속성이고 많은 다른 것들에 의존 할 수 있기 때문에 이것이 제가 가장 큰 도전이라고 생각하는 것입니다. 대부분의 사람들은 실행 경로를 복구하지 않고 디버그하는 방법 만 배웁니다.