기능적 프로그래밍 언어와 명령형 프로그래밍 언어의 차이점은 무엇입니까?


159

C #, Visual Basic, C ++ 및 Java와 같은 OOP (object-oriented programming) 언어를 포함한 대부분의 주류 언어는 명령형 (프로 시추 럴) 프로그래밍을 지원하도록 설계되었지만 Haskell / gofer와 같은 언어는 순전히 기능적입니다. 이 두 프로그래밍 방식의 차이점이 무엇인지 자세히 설명 할 수 있습니까?

프로그래밍 방식을 선택하는 것은 사용자 요구 사항에 달려 있지만 기능적 프로그래밍 언어를 배우는 것이 권장되는 이유는 무엇입니까?



1
이 다른 [post] [1]을 확인하십시오. 차이점을 명확하게 설명합니다. [1] : stackoverflow.com/questions/602444/…
theta

답변:


160

정의 : 명령형 언어는 일련의 문장을 사용하여 특정 목표에 도달하는 방법을 결정합니다. 이 문장들은 각각 차례로 실행될 때 프로그램의 상태를 변경한다고합니다.

예 : Java는 필수 언어입니다. 예를 들어 일련의 숫자를 추가하는 프로그램을 만들 수 있습니다.

 int total = 0;
 int number1 = 5;
 int number2 = 10;
 int number3 = 15;
 total = number1 + number2 + number3; 

각 명령문은 값을 각 변수에 지정하는 것에서 해당 값의 최종 추가에 이르기까지 프로그램의 상태를 변경합니다. 일련의 5 개의 문장을 사용하여 프로그램은 숫자 5, 10 및 15를 더하는 방법을 명시 적으로 알려줍니다.

기능적 언어 : 기능적 프로그래밍 패러다임은 문제 해결에 대한 순수한 기능적 접근을 지원하기 위해 명시 적으로 작성되었습니다. 함수형 프로그래밍은 선언적 프로그래밍의 한 형태입니다.

순수 함수의 장점 : 순수 함수로 함수 변환을 구현하는 주된 이유는 순수 함수를 구성 할 수 있다는 것입니다. 즉, 자체 포함 및 상태 비 저장입니다. 이러한 특성으로 인해 다음과 같은 여러 가지 이점이 있습니다. 가독성 및 유지 관리 성 향상. 각 기능은 주어진 인수에 따라 특정 작업을 수행하도록 설계 되었기 때문입니다. 이 기능은 외부 상태에 의존하지 않습니다.

보다 쉬운 반복 개발. 코드를 리팩토링하기가 쉽기 때문에 디자인 변경이 구현하기가 더 쉬운 경우가 많습니다. 예를 들어, 복잡한 변환을 작성한 다음 변환에서 일부 코드가 여러 번 반복된다는 것을 인식하십시오. 순수한 메소드를 통해 리팩토링하면 부작용에 대해 걱정하지 않고 순수한 메소드를 호출 할 수 있습니다.

더 쉬운 테스트 및 디버깅. 순수 함수는 더 쉽게 테스트 할 수 있으므로 일반적인 값, 유효 엣지 케이스 및 유효하지 않은 엣지 케이스를 사용하여 순수 함수를 호출하는 테스트 코드를 작성할 수 있습니다.

OOP 사용자 또는 명령형 언어의 경우 :

객체 지향 언어는 사물에 대한 고정 된 조작 세트가 있고 코드가 발전함에 따라 주로 새로운 사물을 추가 할 때 좋습니다. 기존 메소드를 구현하는 새 클래스를 추가하면 기존 클래스를 그대로 유지할 수 있습니다.

기능적 언어는 고정 된 사물 집합이 있고 코드가 발전함에 따라 기존 사물에 대한 새로운 작업을 주로 추가 할 때 좋습니다. 기존 데이터 형식으로 계산하는 새 함수를 추가하면 기존 함수가 그대로 유지됩니다.

단점 :

프로그래밍 방식을 선택하는 것은 사용자 요구 사항에 따라 달라 지므로 사용자가 올바른 방식을 선택하지 않을 경우에만 피해가 발생합니다.

진화가 잘못되면 문제가 있습니다.

  • 객체 지향 프로그램에 새로운 작업을 추가하려면 새로운 메소드를 추가하기 위해 많은 클래스 정의를 편집해야 할 수도 있습니다
  • 기능 프로그램에 새로운 종류의 것을 추가하려면 새로운 기능을 추가하기 위해 많은 기능 정의를 편집해야합니다.

10
이 경우 순수한 함수는 수학 함수와 같습니다. 동일한 입력이 항상 동일한 출력에 매핑됩니다. 또한 값이나 값을 반환하는 것 이외의 부작용이 없으므로 컴파일러가 멋진 최적화를 수행 할 수 있으며 충돌 할 것이 없기 때문에 병렬로 함수를 쉽게 실행할 수 있습니다.
WorBlux

따라서 유지 관리 가능하고 테스트 가능한 oop 응용 프로그램을 작성하는 올바른 방법과 모범 사례는 결정적인 마음 상태로 명령 코드를 설계하는 경향이 있습니까?
Kemal Gültekin

4
각 프로그래밍의 기능이 강조 표시되는 텍스트에는 분명한 차이가 없습니다. 절차 적 프로그래밍에 대한 대부분의 설명은 명령형 프로그래밍 텍스트로 교환 할 수 있으며 그 반대도 마찬가지입니다.
AxeEffect

7
이 답변은 함수형 프로그래밍이 무엇인지를 명확하게하려고하지만 순수한 함수가 무엇인지 정의하지 않아도됩니다. 나는 누군가 가이 대답을 읽고 선언적과 절차 적 프로그래밍의 차이점을 알고 자신감을 느끼는 방법을 보지 못했습니다.
Ringo

230

차이점은 다음과 같습니다.

피할 수 없는:

  • 스타트
  • 신발 사이즈 9 1/2를 켭니다.
  • 열쇠 [7]를 보관할 수있는 공간을 주머니에 넣으십시오.
  • 열쇠를 주머니에 넣을 방에 열쇠를 넣습니다.
  • 차고를 입력하십시오.
  • 차고를 엽니 다.
  • 차를 입력하십시오.

... 등등 ...

  • 냉장고에 우유를 넣으십시오.
  • 중지.

기능적 하위 범주 인 선언적 :

  • 유당 소화에 문제가 없다면 우유는 건강 음료입니다.
  • 일반적으로 냉장고에 우유를 저장합니다.
  • 냉장고는 물건을 시원하게 보관하는 상자입니다.
  • 상점은 품목이 판매되는 장소입니다.
  • "판매"는 돈으로 물건을 교환하는 것을 의미합니다.
  • 또한 물건으로 돈을 교환하는 것을 "구매"라고합니다.

... 등등 ...

  • 냉장고에 우유가 있는지 확인하십시오 (필요한 경우-게으른 기능적 언어의 경우).

요약 : 명령형 언어에서는 컴퓨터에서 메모리의 비트, 바이트 및 단어를 변경하는 방법과 순서를 알려줍니다. 기능적인 것에서, 우리는 컴퓨터에게 사물, 행동 등이 무엇인지 알려줍니다. 예를 들어, 0의 계승은 1이고 다른 모든 자연수의 계승은 그 수의 곱과 전임의 계승이라고합니다. 우리는 말하지 않습니다 : n의 계승을 계산하려면 메모리 영역을 예약하고 거기에 1을 저장 한 다음 해당 메모리 영역의 숫자에 숫자 2에서 n을 곱하고 결과를 같은 장소에 저장하십시오. 메모리 영역에는 계승이 포함됩니다.


1
감사합니다. 그것을 보는 좋은 방법입니다.
L-Samuels

5
나는 당신의 설명 @Igno를 좋아했지만 여전히 나에게 불분명합니다. 선언에서 방금 비록 거즈,하지만 여전히 당신은 바로 진행하기 위해 기계 상태로 비트와 메이크업 변경을 변경해야합니다. 어떻게 든 선언이 절차 적 프로그래밍 (C 함수와 같은 )과 유사하지만 여전히 내부적으로 큰 차이가 있습니다. C 기능 이 기능 프로그래밍의 기능 (기계 수준) 과 동일 하지 않습니까?
phoenisx

11
@Igno, Subroto와 마찬가지로 귀하의 설명을 이해하지 못합니다. 작성한 내용을 다음과 같이 요약 할 수 있습니다. 답변 필요 ... 답변 얻기. 어떻게 중요한 비트를 무시하는 것 같습니다. 나는 당신이 어떻게 그 부분을 사용자에게서 숨길 수 있는지 이해하지 못합니다. 어느 시점에서 누군가가 어떻게 수행했는지 알아야합니다 ... 마법사를 영원히 막을 수는 없습니다.
Brett Thomas

3
이것은 내가 함수형 프로그래밍을 이해하는 것이 아닙니다. 함수형 프로그래밍은 함수에서 숨겨진 입력과 출력을 제거하는 것이라고 생각했습니다.
Ringo

7
복잡한 설명.
JoeTidee

14

대부분의 현대 언어는 명령형과 기능면에서 다양하지만 기능적 프로그래밍을 더 잘 이해하려면 java / c #과 같은 기능적 언어가 아닌 명령형 코드와 대조적으로 Haskell과 같은 순수한 기능적 언어의 예를 취하는 것이 가장 좋습니다. 나는 항상 예제로 설명하기가 쉽다고 생각하므로 아래는 하나입니다.

함수형 프로그래밍 : n, n의 계승 계산! 즉, nx (n-1) x (n-2) x ... x 2 X 1

-- | Haskell comment goes like
-- | below 2 lines is code to calculate factorial and 3rd is it's execution  

factorial 0 = 1
factorial n = n * factorial (n - 1)
factorial 3

-- | for brevity let's call factorial as f; And x => y shows order execution left to right
-- | above executes as := f(3) as 3 x f(2) => f(2) as 2 x f(1) => f(1) as 1 x f(0) => f(0) as 1  
-- | 3 x (2 x (1 x (1)) = 6

Haskel을 사용하면 함수 값을 인수 값 수준으로 오버로드 할 수 있습니다. 다음은 명령의 정도를 증가시키는 명령 코드의 예입니다.

//somewhat functional way
function factorial(n) {
  if(n < 1) {
     return 1;
  }
  return n * factorial(n-1);   
}
factorial(3);

//somewhat more imperative way
function imperativeFactor(n) {
  int f = 1
  for(int i = 1; i <= n; i++) {
     f = f * i
  }
  return f;
}

읽기 는 명령형 코드가 부분, 기계 상태 (i in for loop), 실행 순서, 흐름 제어에 어떻게 초점을 맞추는 지 이해하기에 좋은 참고 자료가 될 수 있습니다.

후자의 예제는 대략적으로 첫 번째 부분은 Haskell이 함수를 값 (0)으로 오버로드하는 것과 대조적으로 언어 자체의 제한으로 볼 수 있으며 첫 번째 부분은 순수한 기능적 언어가 아니라고 말할 수 있습니다. 손으로 기능적 프로그램을 지원한다고 말할 수 있습니다. 어느 정도.

공개 : 위 코드 중 어느 것도 테스트 / 실행되지 않았지만 개념을 전달하기에 충분해야합니다. 또한 나는 그러한 수정에 대한 의견을 부탁드립니다 :)


1
그렇지 return n * factorial(n-1);않습니까?
jinawee

@jinawee, 지적 해 주셔서 감사합니다, 나는 그것을 수정했습니다n * (n-1)
old-monk

10

함수형 프로그래밍 은 선언적 프로그래밍의 한 형태로 계산 논리를 설명하며 실행 순서는 완전히 강조되지 않습니다.

문제 :이 생물을 말에서 기린으로 바꾸고 싶습니다.

  • 목을 길게
  • 다리를 길게
  • 스팟 적용
  • 생물에게 검은 혀를 줘
  • 말 꼬리를 제거

각 항목은 순서에 관계없이 동일한 결과를 생성 할 수 있습니다.

명령형 프로그래밍 은 절차 적입니다. 상태와 질서가 중요합니다.

문제점 : 차를 주차하고 싶습니다.

  1. 차고 문의 초기 상태에 유의하십시오.
  2. 차도에서 차를 중지
  3. 차고 문이 닫히면 차고 문을 열고 새로운 상태를 기억하십시오. 그렇지 않으면 계속
  4. 차를 차고로 끌어 들여
  5. 차고 문 닫기

원하는 결과를 얻으려면 각 단계를 수행해야합니다. 차 고문이 닫혀있는 동안 차고 안으로 들어가면 차 고문이 파손됩니다.


비동기와 동기화의 차이점 만 보입니다.
Vladimir Vukanac

@VladimirVukanac 비동기 / 동기화는 프로그래밍 방식이 아닌 메커니즘입니다.
Jakub Keller

2
고마워, 더 연구 할게 문제 1을 문제 2와 동일하게 업데이트하고 "내 차를 주차하고 싶다"고 기능 프로그래밍 방식으로 작성하려면 너무 친절합니까? 그러면 병렬 처리가 제외됩니다.
Vladimir Vukanac

6

함수형 프로그래밍은 "함수를 사용한 프로그래밍"으로, 함수에는 참조 투명도를 포함하여 예상되는 수학적 특성이 있습니다. 이러한 속성들로부터, 추가 속성들이 흐르고, 특히 수학적 증거로 이어지는 대체 가능성에 의해 가능하게되는 친숙한 추론 단계들 (즉, 결과에 대한 신뢰를 정당화).

기능적인 프로그램은 단순한 표현 일뿐입니다.

표현식이 더 이상 참조 적으로 투명하지 않은 (따라서 함수와 값으로 작성되지 않고 자체적으로 함수의 일부가 될 수없는) 명령형 프로그램에서 위치를 주목하여 두 스타일 간의 대비를 쉽게 확인할 수 있습니다. 가장 명백한 두 가지 장소는 다음과 같습니다. 돌연변이 (예 : 변수) 기타 부작용 비 국소 적 제어 흐름 (예 : 예외)

함수와 값으로 구성된이 프로그램 표현의 프레임 워크에는 언어, 개념, "기능 패턴", 조합기 및 다양한 유형 시스템과 평가 알고리즘의 실질적인 패러다임이 구축됩니다.

가장 극단적 인 정의로 C 또는 Java와 같은 거의 모든 언어를 기능적이라고 할 수 있지만 일반적으로 사람들은 구체적으로 관련된 추상화 (예 : 클로저, 불변 값 및 패턴 일치와 같은 구문 보조 도구)가있는 언어를 의미합니다. 함수형 프로그래밍의 사용에 관한 한 펑틴의 사용과 관련이 있으며 부작용없이 코드를 빌드합니다. 증거를 작성하는 데 사용


3

명령형 프로그래밍 스타일은 2005 년부터 2013 년까지 웹 개발에서 실행되었습니다.

명령형 프로그래밍으로 애플리케이션이 수행해야 할 작업을 단계별로 정확하게 나열한 코드를 작성했습니다.

함수형 프로그래밍 스타일은 함수를 결합하는 영리한 방법을 통해 추상화를 생성합니다.

답변에는 선언적 프로그래밍에 대한 언급이 있으며 선언적 프로그래밍에는 우리가 따라야 할 규칙이 나열되어 있다고 말할 것입니다. 그런 다음 애플리케이션에 초기 상태라고하는 것을 제공하고 이러한 규칙이 애플리케이션의 동작 방식을 정의하도록합니다.

이제 이러한 빠른 설명은 이해가되지 않을 것이므로 유추를 통해 명령형 프로그래밍과 선언적 프로그래밍의 차이점을 살펴 보겠습니다.

우리가 소프트웨어를 개발하지 않고 대신 생계를 위해 파이를 굽는다고 상상해보십시오. 아마도 우리는 나쁜 제빵사이며 맛있는 파이를 굽는 방법을 모릅니다.

그래서 우리의 상사는 우리에게 레시피로 알려진 방향 목록을 제공합니다.

레시피는 파이 만드는 법을 알려줄 것입니다. 하나의 레시피는 다음과 같이 명령형으로 작성됩니다.

  1. 밀가루 1 컵을 섞는다
  2. 계란 1 개 추가
  3. 설탕 1 컵 추가
  4. 냄비에 혼합물을 부어
  5. 팬을 오븐에 30 분 350도 동안 넣습니다.

선언적 레시피는 다음을 수행합니다.

밀가루 1 컵, 달걀 1 개, 설탕 1 컵-초기 상태

규칙

  1. 모든 것이 혼합되면 팬에 넣습니다.
  2. 모든 것이 섞이지 않으면 그릇에 넣으십시오.
  3. 팬에 모든 것이 있으면 오븐에 넣으십시오.

따라서 명령 방식은 단계별 접근 방식으로 특징 지워집니다. 1 단계부터 시작하여 2 단계 등으로 이동하십시오.

결국 최종 제품이 생깁니다. 이 파이를 만들면이 재료들을 섞어서 팬과 오븐에 넣고 최종 제품을 얻습니다.

선언적 세계에서는 그 차이가 다릅니다. 선언적 레시피에서는 레시피를 두 개의 개별 파트로 분리하고 변수와 같이 레시피의 초기 상태를 나열하는 하나의 파트로 시작합니다. 여기 변수는 재료의 양과 유형입니다.

우리는 초기 상태 또는 초기 재료를 사용하여 몇 가지 규칙을 적용합니다.

따라서 우리는 초기 상태를 취하여 대황 딸기 파이 등을 먹을 준비가 될 때 까지이 규칙을 반복해서 통과시킵니다.

따라서 선언적 접근 방식에서 이러한 규칙을 올바르게 구성하는 방법을 알아야합니다.

따라서 재료 또는 상태를 검사하고 싶을 때 규칙을 팬에 넣습니다.

초기 상태에서는 아직 재료를 섞지 않았기 때문에 일치하지 않습니다.

따라서 규칙 2는 그들이 섞이지 않으면 그릇에 섞어 말합니다. 예,이 규칙이 적용됩니다.

이제 우리 상태로 혼합 재료 한 그릇이 있습니다.

이제 새로운 상태를 다시 규칙에 적용합니다.

따라서 규칙 1은 재료가 혼합되어 냄비에 놓이면 규칙 1이 적용된다고 말합니다.

이제 우리는 재료가 혼합되어 팬에있는이 새로운 상태를 갖게되었습니다. 규칙 1은 더 이상 관련이 없으며 규칙 2는 적용되지 않습니다.

규칙 3은 재료가 냄비에 있으면 오븐에 넣고 규칙은이 새로운 상태에 적용되는 것입니다.

그리고 우리는 맛있는 핫 애플 파이 또는 다른 것으로 끝납니다.

지금, 당신이 나와 같다면, 왜 우리가 명령형 프로그래밍을하지 않는지 생각할 것입니다. 이것은 말이됩니다.

간단한 흐름은 가능하지만 대부분의 웹 응용 프로그램에는 명령형 프로그래밍 디자인으로는 제대로 캡처 할 수없는보다 복잡한 흐름이 있습니다.

선언적 접근 방식에서는 textInput=“”단일 변수 와 같은 초기 성분 또는 초기 상태를 가질 수 있습니다 .

텍스트 입력이 빈 문자열로 시작될 수 있습니다.

우리는이 초기 상태를 응용 프로그램에 정의 된 일련의 규칙에 적용합니다.

  1. 사용자가 텍스트를 입력하면 텍스트 입력을 업데이트하십시오. 글쎄, 지금은 적용되지 않습니다.

  2. 템플리트가 렌더링되면 위젯을 계산하십시오.

  3. textInput이 업데이트되면 템플릿을 다시 렌더링하십시오.

글쎄,이 중 어느 것도 적용되지 않으므로 프로그램은 이벤트가 발생할 때까지 기다릴 것입니다.

따라서 어느 시점에서 사용자는 텍스트 입력을 업데이트 한 다음 규칙 번호 1을 적용 할 수 있습니다.

우리는 그것을 업데이트 할 수 있습니다 “abcd”

그래서 우리는 방금 텍스트와 textInput 업데이트를 업데이트했습니다. 규칙 번호 2는 적용되지 않습니다. 규칙 번호 3은 텍스트 입력이 업데이트되었는지를 나타냅니다. 방금 발생한 템플릿을 다시 렌더링 한 다음 템플릿을 다시 렌더링하면 규칙 2로 돌아갑니다. 위젯을 계산하면 위젯을 계산할 수 있습니다.

일반적으로 프로그래머로서 우리는보다 선언적인 프로그래밍 디자인을 위해 노력하고 싶습니다.

명령형이 더 명확하고 명백해 보이지만 선언적 접근 방식은 대규모 응용 프로그램에 매우 적합합니다.


2

• 명령형 언어 :

  • 효율적인 실행

  • 복잡한 의미

  • 복잡한 구문

  • 동시성은 프로그래머가 설계

  • 복잡한 테스트, 참조 투명성이 없으며 부작용이 있습니다.

  • 상태가

• 기능 언어 :

  • 간단한 의미론

  • 간단한 구문

  • 덜 효율적인 실행

  • 프로그램을 자동으로 동시에 만들 수 있습니다

  • 간단한 테스트, 참조 투명성, 부작용 없음

  • 상태가 없습니다

1

함수형 프로그래밍을 꼭 필요한 방식으로 표현할 수 있다고 생각합니다.

  • 객체 및 if... else/ switch문에 대한 많은 상태 확인 사용
  • 비동기 성을 처리하기위한 시간 초과 / 대기 메커니즘

이러한 접근 방식에는 큰 문제가 있습니다.

  • 규칙 / 절차가 반복됩니다
  • Statefulness는 부작용 / 실수 가능성을 남깁니다.

객체와 같은 함수 / 메소드를 처리하고 상태 비 저장을 수용하는 함수형 프로그래밍은 내가 믿는 문제를 해결하기 위해 탄생했습니다.

사용 예 : Android, iOS 또는 웹 앱의 로직과 같은 프론트 엔드 애플리케이션 백엔드와의 통신.

명령형 / 절차 적 코드로 기능적 프로그래밍을 시뮬레이션 할 때의 다른 과제 :

  • 경쟁 조건
  • 복잡한 조합 및 이벤트 순서. 예를 들어, 사용자는 뱅킹 앱에서 송금하려고합니다. 1 단계) 다음을 모두 병렬로 수행하고 모든 것이 양호 할 경우에만 진행 a) 사용자가 여전히 양호한 지 확인 (사기, AML) b) 사용자의 잔고가 충분한 지 확인 c) 수취인이 유효한지 (사기, AML) 등. 2 단계) 전송 작업 수행 3 단계) 사용자 잔액 및 / 또는 어떤 종류의 추적에 대한 업데이트 표시. 예를 들어 RxJava를 사용하면 코드가 간결하고 현명합니다. 그것 없이는 많은 코드, 지저분하고 오류가 발생하기 쉬운 코드가 있다고 상상할 수 있습니다.

또한 하루가 끝나면 함수 코드가 컴파일러에 의해 필수적인 절차 적 어셈블리 또는 기계 코드로 변환 될 것이라고 믿습니다. 그러나 어셈블리를 작성하지 않는 한, 인간이 읽기 쉬운 언어로 코드를 작성하는 경우 기능 프로그래밍은 나열된 시나리오에 더 적합한 표현 방법입니다.


-1

나는이 질문이 오래되었고 다른 사람들이 이미 잘 설명했다는 것을 알고 있으며, 간단한 용어로 설명하는 문제를 제시하고 싶습니다.

문제 : 1을 쓰는 중입니다.

해결책: -

명령형 스타일 : =>

    1*1=1
    1*2=2
    1*3=3
    .
    .
    .
    1*n=n 

기능적 스타일 별 : =>

    1
    2
    3
    .
    .
    .
    n

명령형 설명에서는 명령을보다 명확하게 작성하고보다 간단한 방식으로 호출 할 수 있습니다.

기능적 스타일에서와 같이 설명이 필요한 것은 무시됩니다.

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