절차 적 프로그래밍 과 함수형 프로그래밍에 대한 Wikipedia 기사를 읽었 지만 여전히 약간 혼란 스럽습니다. 누군가 그것을 핵심으로 끓일 수 있습니까?
절차 적 프로그래밍 과 함수형 프로그래밍에 대한 Wikipedia 기사를 읽었 지만 여전히 약간 혼란 스럽습니다. 누군가 그것을 핵심으로 끓일 수 있습니까?
답변:
함수형 언어는 (이상적으로) 수학 함수, 즉 n 개의 인수 를 가져 와서 값을 반환하는 함수를 작성할 수 있습니다 . 프로그램이 실행되면이 기능은 필요에 따라 논리적으로 평가됩니다. 1
반면 절차 언어는 일련의 순차적 단계를 수행합니다. (순차 논리를 연속 전달 스타일 이라는 기능적 논리로 변환하는 방법이 있습니다 .)
결과적으로 순전히 기능적인 프로그램 은 입력에 대해 항상 동일한 값 을 산출 하며 평가 순서는 잘 정의되지 않습니다. 즉, 사용자 입력 또는 임의 값과 같은 불확실한 값은 순전히 기능적인 언어로 모델링하기가 어렵습니다.
1 이 답변의 다른 모든 내용은 일반화입니다. 이 속성은 결과가 호출되는 곳이 아닌 결과가 필요할 때 계산을 평가하는 것을 "게으름"이라고합니다. 모든 기능적 언어가 실제로 보편적으로 게으른 것도 아니고 게으름도 기능적 프로그래밍으로 제한되지 않습니다. 오히려, 여기에 주어진 설명은 구별되고 반대되는 범주가 아니라 유동적 인 아이디어 인 서로 다른 프로그래밍 스타일에 대해 생각하기위한“정신적 틀”을 제공합니다.
기본적으로 두 가지 스타일은 음과 양입니다. 하나는 조직되어 있고 다른 하나는 혼란입니다. 기능적 프로그래밍이 확실한 선택 인 상황이 있으며 다른 상황은 절차 적 프로그래밍이 더 나은 선택입니다. 그렇기 때문에 최근 두 가지 프로그래밍 스타일을 모두 수용하는 새로운 버전의 언어가 두 개 이상 있습니다. ( 펄 6 과 D 2 )
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;
}
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
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 }
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
)
sub postfix:<!> ($n) { [*] 1..$n }
No operation can have side effects
정교하게 설명해 주 시겠습니까?
sub foo( $a, $b ){ ($a,$b).pick }
← 항상 동일한 입력에 대해 동일한 출력을 반환하지는 않지만 다음은 그렇지 않습니다.sub foo( $a, $b ){ $a + $b }
나는 다른 곳 에서이 정의를 본 적이 없지만 여기에 주어진 차이점을 상당히 잘 요약한다고 생각합니다.
함수형 프로그래밍은 표현식에 중점을 둡니다.
절차 적 프로그래밍은 문장에 중점을 둡니다 .
표현식에는 값이 있습니다. 기능적 프로그램은 가치있는 표현으로서 컴퓨터가 수행해야 할 일련의 명령입니다.
명령문에는 값이없고 대신 일부 개념적 기계의 상태를 수정합니다.
순전히 기능적인 언어에서는 상태를 조작 할 수있는 방법이 없다는 의미에서 어떤 진술도 없을 것입니다 (그들은 여전히 "statement"라는 구문 구조를 가질 수 있지만, 상태를 조작하지 않으면 이러한 의미에서 진술이라고하지 않습니다. ). 순전히 절차적인 언어에서는 표현이 없으며 모든 것은 기계의 상태를 조작하는 명령 일 것입니다.
Haskell은 상태를 조작 할 방법이 없기 때문에 순전히 기능적인 언어의 예입니다. 프로그램의 모든 것은 머신의 레지스터와 메모리의 상태를 조작하는 명령문이기 때문에 머신 코드는 순전히 절차 적 언어의 예입니다.
혼란스러운 부분은 대다수의 프로그래밍 언어에 표현과 문장이 모두 포함되어 있어 패러다임을 혼합 할 수 있다는 것입니다. 언어는 문장 대 표현의 사용을 장려하는 정도에 따라 기능적 또는 절차 적 언어로 분류 될 수 있습니다.
예를 들어, 함수 호출은 표현식이기 때문에 C는 COBOL보다 기능적이지만 COBOL에서 서브 프로그램 호출은 명령문 (공유 변수의 상태를 조작하고 값을 리턴하지 않음)입니다. Python은 단락 평가를 사용하여 조건부 논리를 표현식으로 표현할 수 있기 때문에 C보다 더 기능적입니다 (if 문이 아닌 test && path1 || path2). 체계의 모든 것이 표현이기 때문에 체계는 파이썬보다 더 기능적입니다.
절차 적 패러다임을 장려하는 언어로 기능적 스타일로 작성할 수 있으며 그 반대도 마찬가지입니다. 언어에서 권장하지 않는 패러다임으로 글을 쓰는 것이 더 어려워지고 어색합니다.
컴퓨터 과학에서 기능 프로그래밍은 계산을 수학 함수의 평가로 취급하고 상태 및 변경 가능한 데이터를 피하는 프로그래밍 패러다임입니다. 상태의 변화를 강조하는 절차 적 프로그래밍 스타일과 달리 함수의 적용을 강조합니다.
GetUserContext()
함수에 넣지 않고 사용자 컨텍스트가 전달됩니다.이 함수형 프로그래밍입니까? 미리 감사드립니다.
절차 적 / 기능적 / 객관적 프로그래밍은 문제에 접근하는 방법에 관한 것이라고 생각합니다.
첫 번째 스타일은 모든 단계를 계획하고 한 번에 한 단계 (절차)를 구현하여 문제를 해결합니다. 다른 한편으로, 함수형 프로그래밍은 문제가 하위 문제로 나뉘고 각 하위 문제가 해결되고 (해당 하위 문제를 해결하기위한 함수 작성) 결과가 결합되는 분할 및 정복 방식을 강조합니다. 전체 문제에 대한 답을 만드십시오. 마지막으로, 객관적 프로그래밍은 컴퓨터 내에 각각 고유 한 특성을 가지고 있으며 다른 객체와 상호 작용하는 많은 객체가있는 미니 월드를 만들어 실제 세계를 모방합니다. 이러한 상호 작용에서 결과가 나타납니다.
각 스타일의 프로그래밍에는 고유 한 장단점이 있습니다. 따라서 "순수한 프로그래밍"과 같은 것을 수행하는 것 (즉, 순전히 절차 적-아무도 이상한 일을하거나 전혀 기능적이거나 순전히 객관적인 일을하지 않습니다)은 일부 초급 문제를 제외하고는 불가능하지는 않지만 매우 어렵습니다. 프로그래밍 스타일의 장점을 보여 주도록 설계되었습니다 (따라서 순수성을 좋아하는 사람들을 "weenie": D라고합니다).
그런 다음 이러한 스타일에서 각 스타일에 맞게 최적화 된 프로그래밍 언어가 있습니다. 예를 들어, 어셈블리는 모든 절차에 관한 것입니다. 자, 대부분의 초기 언어는 C, Pascal과 같은 Asm뿐만 아니라 절차 적입니다 (Fortran, 나는 들었습니다). 그런 다음 우리는 객관적인 학교에서 모두 유명한 Java를 가지고 있습니다 (실제로 Java와 C #은 "돈 지향"이라는 클래스에 있지만 다른 토론의 대상입니다). 또한 목표는 스몰 토크입니다. 기능 학교에서는 "거의 기능적"(일부는 불완전한 것으로 간주) Lisp 가족 및 ML 가족과 많은 "순전히 기능적인"Haskell, Erlang 등을 갖습니다. 그런데 Perl, Python과 같은 많은 일반 언어가 있습니다. 루비
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) .
Konrad의 의견을 확대하려면 :
결과적으로 순전히 기능적인 프로그램은 입력에 대해 항상 동일한 값을 산출하며 평가 순서는 잘 정의되지 않습니다.
이 때문에 기능 코드는 일반적으로 병렬화가 더 쉽습니다. 함수의 부작용은 (일반적으로) 없으며, 일반적으로 인수에 대해 행동하기 때문에 많은 동시성 문제가 사라집니다.
함수형 프로그래밍은 코드가 올바른지 증명할 수 있어야 할 때도 사용됩니다 . 이것은 절차 적 프로그래밍과는 훨씬 더 어렵습니다 (기능적으로 쉽지는 않지만 여전히 더 쉽습니다).
면책 조항 : 몇 년 동안 기능 프로그래밍을 사용하지 않았으며 최근에 다시보기 시작 했으므로 여기에 완전히 정확하지 않을 수 있습니다. :)
절차 적 언어는 변수를 사용하여 상태를 추적하는 경향이 있으며 일련의 단계로 실행되는 경향이 있습니다. 순전히 기능적인 언어는 상태를 추적하지 않고 불변 값을 사용하며 일련의 종속성으로 실행되는 경향이 있습니다. 대부분의 경우 호출 스택의 상태에는 절차 코드의 상태 변수에 저장된 정보와 동일한 정보가 포함됩니다.
재귀는 함수형 프로그래밍의 고전적인 예입니다.
콘라드는 말했다 :
결과적으로 순전히 기능적인 프로그램은 입력에 대해 항상 동일한 값을 산출하며 평가 순서는 잘 정의되지 않습니다. 즉, 사용자 입력 또는 임의 값과 같은 불확실한 값은 순전히 기능적인 언어로 모델링하기 어렵습니다.
순전히 기능적인 프로그램의 평가 순서는 (특히 게으름으로 인해) 추론하기 어려울 수 있지만 중요하지 않다고 말하면 프로그램이 진행되고 있는지 알 수없는 것처럼 들립니다. 전혀 작동합니다!
아마도 더 나은 설명은 함수형 프로그램의 제어 흐름이 함수의 인수 값이 필요할 때를 기반으로한다는 것입니다. 이것에 대해 좋은 점은 잘 쓰여진 프로그램에서, 상태가 명시 될 것을 : 각 기능은 매개 변수 대신 임의의로의 입력을 나열 munging 글로벌 상태. 따라서 어떤 수준에서는 한 번에 하나의 기능에 대한 평가 순서를 추론하기가 더 쉽습니다 . 각 기능은 나머지 우주를 무시하고 필요한 작업에 집중할 수 있습니다. 결합되면 기능은 독립적으로 작동하는 것과 동일하게 작동합니다 [1].
... 사용자 입력 또는 임의 값과 같은 불확실한 값은 순전히 기능적인 언어로 모델링하기가 어렵습니다.
순전히 기능적인 프로그램의 입력 문제에 대한 해결책 은 충분히 강력한 추상화를 사용하여 명령형 언어를 DSL 로 포함시키는 것 입니다. 명령형 (또는 순수한 기능이 아닌) 언어에서는 암묵적으로 상태를 "속임수"로 전달할 수 있으며 평가 순서가 명시 적이든 (원하든 원하지 않든) 필요하지 않습니다. 모든 기능에 대한 모든 매개 변수의 이러한 "속임수"및 강제 평가로 인해 명령 언어에서 1) 고유 한 제어 흐름 메커니즘 (매크로없이)을 생성하는 기능이 손실됩니다 .2) 코드는 본질적으로 스레드 안전 및 / 또는 병렬화 가능하지 않습니다 기본적으로, 3) 실행 취소 (시간 여행)와 같은 것을 구현하려면 신중한 작업이 필요합니다. 잊어 버렸습니다 — "무료".
나는 이것이 열광적 인 것처럼 들리지 않기를 원하며 단지 관점을 추가하고 싶었습니다. C # 3.0과 같은 강력한 언어의 명령형 프로그래밍, 특히 혼합 패러다임 프로그래밍은 여전히 작업을 수행하는 데 매우 효과적인 방법이며 은총 알이 없습니다 .
[1] ... 메모리 사용과 관련하여 가능한 경우를 제외하고 (Haskell의 foldl 및 foldl '참조).
기회가 있다면 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]
차이점을 이해하려면 절차 적 프로그래밍 과 기능적 프로그래밍의 "대부"패러다임이 명령형 프로그래밍 이라는 것을 이해해야합니다 .
기본적으로 절차 적 프로그래밍은 추상화의 주요 방법이 "프로 시저"인 명령형 프로그램을 구성하는 방법 일뿐입니다. (또는 일부 프로그래밍 언어에서는 "기능"). 객체 지향 프로그래밍조차도 명령형 프로그램을 구성하는 또 다른 방법 일뿐입니다. 여기서 상태는 객체로 캡슐화되어 "현재 상태"를 가진 객체가됩니다. 프로그래머는 상태를 조작하거나 업데이트합니다.
이제 함수형 프로그래밍과 관련하여 요점 은 취해야 할 값과 이러한 값의 전송 방법을 식별하는 것입니다. (따라서 함수를 일급 값으로 가져 와서 다른 함수에 매개 변수로 전달하므로 상태가 없으며 변경 가능한 데이터가 없습니다).
추신 : 모든 프로그래밍 패러다임을 이해하면 모든 프로그래밍 패러다임을 이해해야합니다.
추신 : 프로그래밍 패러다임은 하루 종일 문제 해결에 대한 다른 접근 방식입니다.
추신 : 이 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
기능이 있습니다).