IT story

절차 적 프로그래밍과 기능적 프로그래밍의 차이점은 무엇입니까?

hot-time 2020. 4. 20. 20:34
반응형

절차 적 프로그래밍과 기능적 프로그래밍의 차이점은 무엇입니까? [닫은]


절차 적 프로그래밍함수형 프로그래밍에 대한 Wikipedia 기사를 읽었 지만 여전히 약간 혼란 스럽습니다. 누군가 그것을 핵심으로 끓일 수 있습니까?


함수형 언어는 (이상적으로) 수학 함수, 즉 n 개의 인수 를 가져 와서 값을 반환하는 함수를 작성할 수 있습니다 . 프로그램이 실행되면이 기능은 필요에 따라 논리적으로 평가됩니다. 1

반면 절차 언어는 일련의 순차적 단계를 수행합니다. (순차 논리를 연속 전달 스타일 이라는 기능적 논리로 변환하는 방법이 있습니다 .)

결과적으로 순전히 기능적인 프로그램 은 입력에 대해 항상 동일한 값산출 하며 평가 순서는 잘 정의되지 않습니다. 즉, 사용자 입력 또는 임의 값과 같은 불확실한 값은 순전히 기능적인 언어로 모델링하기가 어렵습니다.


1 이 답변의 다른 모든 내용은 일반화입니다. 호출되는 위치가 아닌 결과가 필요할 때 계산을 평가하는이 속성을 "게으름"이라고합니다. 모든 기능적 언어가 실제로 보편적으로 게으른 것도 아니고 게으름도 기능적 프로그래밍으로 제한되지 않습니다. 오히려, 여기에 주어진 설명은 구별되고 반대되는 범주가 아니라 유동적 인 아이디어 인 서로 다른 프로그래밍 스타일에 대해 생각하기위한“정신적 틀”을 제공합니다.


기본적으로 두 가지 스타일은 음과 양입니다. 하나는 조직되고 다른 하나는 혼란입니다. 기능적 프로그래밍이 확실한 선택 인 상황이 있고 다른 상황은 절차 적 프로그래밍이 더 나은 선택입니다. 그렇기 때문에 최근 두 가지 프로그래밍 스타일을 모두 수용하는 새로운 버전으로 출시 된 언어가 두 개 이상 있습니다. ( 펄 6D 2 )

절차 :

  • 루틴의 출력이 항상 입력과 직접적인 상관 관계가있는 것은 아닙니다.
  • 모든 것은 특정 순서로 이루어집니다.
  • 루틴을 실행하면 부작용이있을 수 있습니다.
  • 선형 방식으로 솔루션 구현을 강조하는 경향이 있습니다.

펄 6

sub factorial ( UInt:D $n is copy ) returns UInt {

  # modify "outside" state
  state $call-count++;
  # in this case it is rather pointless as
  # it can't even be accessed from outside

  my $result = 1;

  loop ( ; $n > 0 ; $n-- ){

    $result *= $n;

  }

  return $result;
}

D 2

int factorial( int n ){

  int result = 1;

  for( ; n > 0 ; n-- ){
    result *= n;
  }

  return result;
}

기능의:

  • 종종 재귀 적입니다.
  • 주어진 입력에 대해 항상 동일한 출력을 반환합니다.
  • 평가 순서는 일반적으로 정의되지 않습니다.
  • 상태 비 저장이어야합니다. 즉, 아무 작업도 부작용을 가질 수 없습니다.
  • 병렬 실행에 적합
  • 나누고 정복하는 접근법을 강조하는 경향이 있습니다.
  • 지연 평가 기능이있을 수 있습니다.

하스켈

( Wikipedia 에서 복사 );

fac :: Integer -> Integer

fac 0 = 1
fac n | n > 0 = n * fac (n-1)

또는 한 줄로 :

fac n = if n > 0 then n * fac (n-1) else 1

펄 6

proto sub factorial ( UInt:D $n ) returns UInt {*}

multi sub factorial (  0 ) { 1 }
multi sub factorial ( $n ) { $n * samewith $n-1 } # { $n * factorial $n-1 }

D 2

pure int factorial( invariant int n ){
  if( n <= 1 ){
    return 1;
  }else{
    return n * factorial( n-1 );
  }
}

사이드 노트 :

팩토리얼은 실제로 서브 루틴을 만들 때와 같은 방식으로 Perl 6에서 새 연산자를 만드는 것이 얼마나 쉬운지를 보여주는 일반적인 예입니다. 이 기능은 Perl 6에 세분화되어 Rakudo 구현의 대부분의 연산자가이 방식으로 정의됩니다. 또한 기존 연산자에 고유 한 다중 후보를 추가 할 수도 있습니다.

sub postfix:< ! > ( UInt:D $n --> UInt )
  is tighter(&infix:<*>)
  { [*] 2 .. $n }

say 5!; # 120␤

이 예는 또한 숫자 생성 곱셈 연산자와 결합 된 범위 작성 2..$n메타 오퍼레이터 ( [ OPERATOR ] LIST) 범위 작성 ( )을 보여줍니다 . ( *)
또한 --> UInt서명 대신 서명을 넣을 수 있음을 보여줍니다 returns UInt.

( 인수없이 호출 2하면 곱하기 "연산자"가 반환 되므로 범위를 시작하여 벗어날 수 있습니다 1)


나는 다른 곳 에서이 정의를 본 적이 없지만 여기에 주어진 차이점을 상당히 잘 요약한다고 생각합니다.

함수형 프로그래밍은 표현식에 중점을 둡니다.

절차 적 프로그래밍은 문장에 중점을 둡니다 .

표현식에는 값이 있습니다. 기능적 프로그램은 가치있는 표현으로서 컴퓨터가 수행해야 할 일련의 명령입니다.

명령문에는 값이없고 대신 일부 개념적 기계의 상태를 수정합니다.

순전히 기능적인 언어에서는 상태를 조작 할 수있는 방법이 없다는 의미에서 어떤 진술도 없을 것입니다 (여전히 "statement"라는 구문 구조가있을 수 있지만, 상태를 조작하지 않는 한,이 의미에서 진술이라고하지 않습니다. ). 순전히 절차적인 언어에서는 표현이 없을 것입니다. 모든 것은 기계의 상태를 조작하는 명령 일 것입니다.

Haskell은 상태를 조작 할 방법이 없기 때문에 순전히 기능적인 언어의 예입니다. 프로그램의 모든 것은 머신의 레지스터와 메모리의 상태를 조작하는 명령문이기 때문에 머신 코드는 순전히 절차 적 언어의 예입니다.

혼란스러운 부분은 대다수의 프로그래밍 언어에 표현과 문장이 모두 포함되어 있어 패러다임을 혼합 할 수 있다는 것입니다. 언어는 문장 대 표현의 사용을 장려하는 정도에 따라 기능적 또는 절차 적 언어로 분류 될 수 있습니다.

예를 들어, 함수 호출은 표현식이기 때문에 C는 COBOL보다 기능적이지만 COBOL에서 서브 프로그램 호출은 명령문 (공유 변수의 상태를 조작하고 값을 리턴하지 않음)입니다. Python은 단락 평가를 사용하여 조건부 논리를 표현식으로 표현할 수 있기 때문에 C보다 더 기능적입니다 (if 문이 아닌 test && path1 || path2). 체계의 모든 것이 표현이기 때문에 체계는 파이썬보다 더 기능적입니다.

절차 적 패러다임을 장려하는 언어로 기능적 스타일로 작성할 수 있으며 그 반대의 경우도 마찬가지입니다. 언어에서 권장하지 않는 패러다임으로 글을 쓰는 것이 더 어려워지고 어색합니다.


컴퓨터 과학에서 기능 프로그래밍은 계산을 수학 함수의 평가로 취급하고 상태 및 변경 가능한 데이터를 피하는 프로그래밍 패러다임입니다. 상태의 변화를 강조하는 절차 적 프로그래밍 스타일과 달리 함수의 적용을 강조합니다.


절차 적 / 기능적 / 객관적 프로그래밍은 문제에 접근하는 방법에 관한 것이라고 생각합니다.

첫 번째 스타일은 모든 단계를 계획하고 한 번에 한 단계 (절차)를 구현하여 문제를 해결합니다. 다른 한편으로, 함수형 프로그래밍은 문제가 하위 문제로 나누어지고 각 하위 문제가 해결되고 (해당 하위 문제를 해결하기위한 함수 작성) 결과가 다음과 같이 결합되는 분할 및 정복 방식을 강조합니다. 전체 문제에 대한 답을 만드십시오. 마지막으로, 객관적 프로그래밍은 컴퓨터 내에 각각 고유 한 특성을 가지고 있으며 다른 객체와 상호 작용하는 많은 객체가있는 미니 월드를 만들어 실제 세계를 모방합니다. 이러한 상호 작용에서 결과가 나타납니다.

각 스타일의 프로그래밍에는 고유 한 장단점이 있습니다. 그러므로 "순수한 프로그래밍"과 같은 것을 수행하는 것 (즉, 순전히 절차 적-아무도 이상하지 않거나 전혀 기능적이거나 순전히 객관적인 방법은 아님)은 일부 초급 문제를 제외하고는 불가능하지는 않지만 매우 어렵다 프로그래밍 스타일의 장점을 보여 주도록 설계되었습니다 (따라서 순수성을 좋아하는 사람들을 "weenie": D라고합니다).

그런 다음 이러한 스타일에서 각 스타일에 맞게 최적화 된 프로그래밍 언어가 있습니다. 예를 들어, 어셈블리는 모든 절차에 관한 것입니다. 자, 대부분의 초기 언어는 C, Pascal과 같은 Asm뿐만 아니라 절차 적입니다 (Fortran, 들었습니다). 그런 다음 우리는 객관적인 학교에서 모두 유명한 Java를 가지고 있습니다 (실제로 Java와 C #은 "돈 지향"이라는 클래스에 있지만 다른 토론의 대상입니다). 또한 목표는 스몰 토크입니다. 기능 학교에서는 "거의 기능적"(일부는 불완전한 것으로 간주) Lisp 가족과 ML 가족 및 많은 "순전히 기능적인"Haskell, Erlang 등을 갖습니다. 그런데 Perl, Python과 같은 많은 일반 언어가 있습니다. 루비


Konrad의 의견을 확대하려면 :

결과적으로 순전히 기능적인 프로그램은 입력에 대해 항상 동일한 값을 산출하며 평가 순서는 잘 정의되지 않습니다.

이 때문에 기능 코드는 일반적으로 병렬화가 더 쉽습니다. 함수의 부작용은 (일반적으로) 없으며, 일반적으로 인수에 대해 행동하기 때문에 많은 동시성 문제가 사라집니다.

함수형 프로그래밍은 코드가 올바른지 증명할 수 있어야 할 때도 사용됩니다 . 이것은 절차 적 프로그래밍과는 훨씬 더 어렵다 (기능적으로 쉽지는 않지만 여전히 쉽다).

면책 조항 : 몇 년 동안 함수형 프로그래밍을 사용하지 않았으며 최근에 다시 살펴보기 시작 했으므로 여기에 완전히 정확하지 않을 수 있습니다. :)


내가 여기서 실제로 보지 못했던 것 중 하나는 Haskell과 같은 현대의 기능적 언어가 명시 적 재귀보다 흐름 제어를위한 일급 함수에 더 많이 있다는 것입니다. 위에서 한 것처럼 Haskell에서 계승을 재귀 적으로 정의 할 필요는 없습니다. 나는 같은 생각

fac n = foldr (*) 1 [1..n]

완벽하게 관용적 인 구조이며 명시 적 재귀를 사용하는 것보다 루프를 사용하는 것에 훨씬 가깝습니다.


함수형 프로그래밍은 전역 변수 를 사용 하지 않는 절차 형 프로그래밍과 동일합니다 .


함수형 프로그래밍

num = 1 
def function_to_add_one(num):
    num += 1
    return num


function_to_add_one(num)
function_to_add_one(num)
function_to_add_one(num)
function_to_add_one(num)
function_to_add_one(num)

#Final Output: 2

절차 적 프로그래밍

num = 1 
def procedure_to_add_one():
    global num
    num += 1
    return num


procedure_to_add_one()
procedure_to_add_one()
procedure_to_add_one()
procedure_to_add_one()
procedure_to_add_one()

#Final Output: 6

function_to_add_one 기능이다

procedure_to_add_one 절차이다

함수를 5 번 실행하더라도 2 를 반환 할 때마다

당신이 실행하는 경우 절차를 다섯 번, 다섯 번째 실행의 끝에서 당신에게 줄 것이다 (6) .


절차 적 언어는 변수를 사용하여 상태를 추적하는 경향이 있으며 일련의 단계로 실행되는 경향이 있습니다. 순전히 기능적인 언어는 상태를 추적하지 않고 불변 값을 사용하며 일련의 종속성으로 실행되는 경향이 있습니다. 대부분의 경우 호출 스택의 상태에는 절차 코드의 상태 변수에 저장된 정보와 동일한 정보가 포함됩니다.

재귀는 함수형 프로그래밍의 고전적인 예입니다.


콘라드는 말했다 :

결과적으로 순전히 기능적인 프로그램은 입력에 대해 항상 동일한 값을 산출하며 평가 순서는 잘 정의되지 않습니다. 즉, 사용자 입력 또는 임의 값과 같은 불확실한 값은 순전히 기능적인 언어로 모델링하기가 어렵습니다.

순전히 기능적인 프로그램의 평가 순서는 (특히 게으름으로 인해) 추론하기가 어려울 수 있지만 중요하지 않다고 말하면 프로그램이 진행되고 있는지 알 수없는 것처럼 들립니다. 전혀 작동합니다!

아마도 더 나은 설명은 함수형 프로그램의 제어 흐름이 함수의 인수 값이 필요할 때를 기반으로한다는 것입니다. 잘 작성된 프로그램에서 상태는 명백해집니다. 각 함수는 입력을 임의로 글로벌 상태로 뭉치지 않고 매개 변수로 나열합니다 . 따라서 어떤 수준에서는 한 번에 하나의 기능에 대한 평가 순서를 추론하기가 더 쉽습니다 . 각 기능은 나머지 우주를 무시하고 필요한 작업에 집중할 수 있습니다. 결합되면 기능은 독립적으로 작동하는 것과 동일하게 작동합니다 [1].

... 사용자 입력 또는 임의 값과 같은 불확실한 값은 순전히 기능적인 언어로 모델링하기가 어렵습니다.

순전히 기능적인 프로그램의 입력 문제에 대한 해결책 은 충분히 강력한 추상화를 사용하여 명령형 언어를 DSL 로 포함시키는 것 입니다. 명령형 (또는 순수하지 않은 기능) 언어에서는 암시 적으로 "속임수"를 사용하여 상태를 전달할 수 있으며 평가 순서가 명시 적으로 (좋아하든 원하지 않든) 필요하지 않습니다. 모든 기능에 대한 모든 매개 변수의 이러한 "속임수"및 강제 평가로 인해 명령 언어에서 1) 고유 한 제어 흐름 메커니즘 (매크로없이)을 생성하는 기능이 손실됩니다 .2) 코드는 본질적으로 스레드 안전 및 / 또는 병렬화 가능하지 않습니다 기본적으로, 3) 실행 취소 (시간 여행)와 같은 것을 구현하려면 신중한 작업이 필요합니다 (제국 프로그래머는 이전 가치를 되찾기위한 레시피를 저장해야합니다!), 순수 기능 프로그래밍은 이러한 모든 것을 구매합니다. 잊어 버렸습니다 — "무료".

나는 이것이 열광적 인 것처럼 들리지 않기를 원하며 단지 관점을 추가하고 싶었습니다. C # 3.0과 같은 강력한 언어의 명령형 프로그래밍, 특히 혼합 패러다임 프로그래밍은 여전히 ​​작업을 수행하는 데 매우 효과적인 방법이며 은총 알이 없습니다 .

[1] ... 메모리 사용과 관련하여 가능한 경우를 제외하고 (Haskell의 foldl 및 foldl '참조).


Konrad의 의견을 확대하려면 :

평가 순서가 명확하지 않다

일부 기능적 언어에는 지연 평가 (Lazy Evaluation)가 있습니다. 이는 값이 필요할 때까지 함수가 실행되지 않음을 의미합니다. 그 때까지 함수 자체가 전달됩니다.

절차 적 언어는 1 단계 2 단계 3 단계입니다. 2 단계에서 2 + 2를 추가하면 바로 수행됩니다. 게으른 평가에서는 add 2 + 2라고 말하지만 결과가 사용되지 않으면 절대로 추가하지 않습니다.


기회가 있다면 Lisp / Scheme의 사본을 받고 그 안에 몇 가지 프로젝트를 수행하는 것이 좋습니다. 최근에 악몽이 된 아이디어의 대부분은 Lisp에서 기능 프로그래밍, 연속 (폐쇄), 가비지 수집, XML까지 표현되었습니다.

이것이 현재의 모든 아이디어를 시작하고 상징적 계산과 같은 몇 가지 아이디어를 더 잘 이해할 수있는 좋은 방법입니다.

함수형 프로그래밍의 장점과 장점이 무엇인지 알아야합니다. 모든 것에 좋지 않습니다. 일부 문제는 부작용 측면에서 가장 잘 표현되며, 동일한 질문은 요청시기에 따라 다른 답변을 제공합니다.


@Creighton :

Haskell에는 product 라는 라이브러리 함수가 있습니다 .

prouduct list = foldr 1 (*) list

또는 간단히 :

product = foldr 1 (*)

그래서 "아이디 오 매틱"계승

fac n = foldr 1 (*)  [1..n]

간단하게

fac n = product [1..n]

절차 적 프로그래밍은 일련의 명령문과 조건부 구성을 기능이 아닌 값인 인수에 대해 매개 변수화 된 프로 시저라고하는 별도의 블록으로 나눕니다.

함수형 프로그래밍은 함수가 일급 값이라는 점을 제외하고는 동일하므로 다른 함수에 인수로 전달되어 함수 호출의 결과로 반환 될 수 있습니다.

함수형 프로그래밍은이 해석에서 절차 적 프로그래밍의 일반화입니다. 그러나 소수자들은 "기능 프로그래밍"을 부작용이없는 것으로 해석하는데, 이는 하스켈을 제외한 모든 주요 기능 언어와는 다르지만 부작용이 없음을 의미합니다.


차이점을 이해하려면 절차 적 프로그래밍 과 기능적 프로그래밍의 "대부"패러다임이 명령형 프로그래밍 이라는 것을 이해해야합니다 .

기본적으로 절차 적 프로그래밍은 추상화의 주요 방법이 "프로 시저"인 명령형 프로그램을 구성하는 방법 일뿐입니다. (또는 일부 프로그래밍 언어에서는 "기능"). 객체 지향 프로그래밍조차도 명령형 프로그램을 구성하는 또 다른 방법 일뿐입니다. 여기서 상태는 객체에 캡슐화되어 "현재 상태"를 가진 객체가됩니다.이 객체에는 일련의 함수, 메소드 및 기타 기능이 있습니다 프로그래머는 상태를 조작하거나 업데이트합니다.

이제 함수형 프로그래밍과 관련하여 요점 은 취해야 할 값과 이러한 값의 전송 방법을 식별하는 것입니다. (따라서 함수를 일급 값으로 가져 와서 다른 함수에 매개 변수로 전달하므로 상태가 없으며 변경 가능한 데이터가 없습니다).

추신 : 모든 프로그래밍 패러다임을 이해하면 모든 프로그래밍 패러다임을 이해해야합니다.

PSS : 하루가 끝날 무렵 프로그래밍 패러다임은 문제 해결을위한 다른 접근법 일뿐입니다.

추신 : quora 대답은 좋은 설명이 있습니다.


여기에 답은 관용적 기능 프로그래밍을 보여주지 않습니다. 재귀 팩토리얼 답변은 FP에서 재귀를 나타내는 데 유용하지만 대부분의 코드는 재귀 적이 지 않으므로 답변이 완전히 대표적이라고 생각하지 않습니다.

문자열 배열이 있고 각 문자열이 "5"또는 "-200"과 같은 정수를 나타냅니다. 내부 테스트 사례와 비교하여이 문자열 배열을 확인하려고합니다 (정수 비교 사용). 두 솔루션 모두 아래에 표시되어 있습니다

절차 적

arr_equal(a : [Int], b : [Str]) -> Bool {
    if(a.len != b.len) {
        return false;
    }

    bool ret = true;
    for( int i = 0; i < a.len /* Optimized with && ret*/; i++ ) {
        int a_int = a[i];
        int b_int = parseInt(b[i]);
        ret &= a_int == b_int;  
    }
    return ret;
}

기능의

eq = i, j => i == j # This is usually a built-in
toInt = i => parseInt(i) # Of course, parseInt === toInt here, but this is for visualization

arr_equal(a : [Int], b : [Str]) -> Bool =
    zip(a, b.map(toInt)) # Combines into [Int, Int]
   .map(eq)
   .reduce(true, (i, j) => i && j) # Start with true, and continuously && it with each value

순수한 기능 언어는 일반적으로 연구 언어이지만 (실제로 무료 부작용을 좋아하므로) 실제 절차 언어는 적절한 경우 훨씬 간단한 기능 구문을 사용합니다.

일반적으로 Lodash 와 같은 외부 라이브러리를 사용하거나 Rust 와 같은 최신 언어로 내장되어 있습니다 . 함수형 프로그래밍의 무거운는 같은 기능 / 개념을 완료 map, filter, reduce, currying, partial,의 마지막 세 당신이 더 이해를 찾아 볼 수 있습니다.

추가

일반적으로 컴파일러는 일반적으로 함수 호출 오버 헤드가 너무 높으므로 함수 버전을 절차 버전으로 변환하는 방법을 일반적으로 해결해야합니다. 표시된 계승과 같은 재귀 사례는 꼬리 호출 과 같은 트릭을 사용 하여 O (n) 메모리 사용을 제거합니다. 부작용이 없다는 사실은 함수형 컴파일러 가 마지막에 수행 된 && ret경우에도 최적화 를 구현할 수 있도록합니다 .reduce. JS에서 Lodash를 사용하면 분명히 최적화가 허용되지 않으므로 성능에 영향을 미칩니다 (일반적으로 웹 개발에는 관심이 없습니다). Rust와 같은 언어는 내부적으로 최적화됩니다 (그리고 최적화 try_fold를 지원하는 && ret기능이 있습니다).

참고 URL : https://stackoverflow.com/questions/23277/what-is-the-difference-between-procedural-programming-and-functional-programming

반응형

'IT story' 카테고리의 다른 글

레일스 매개 변수 설명?  (0) 2020.04.20
스칼라 2.8 탈주  (0) 2020.04.20
SQL Server Management Studio 2012는 어디에 있습니까?  (0) 2020.04.20
고정 헤더가있는 HTML 테이블?  (0) 2020.04.20
구조 x = x ||  (0) 2020.04.20