퍼스트 클래스 기능
First-class function컴퓨터 공학에서 프로그래밍 언어는 기능을 1등급 시민으로 취급하면 1등급 기능을 가진다고 한다.이는 언어가 함수를 다른 함수에 인수로서 전달하고, 다른 함수의 값으로 반환하고, 변수에 할당하거나 데이터 [1]구조에 저장하는 것을 지원한다는 것을 의미합니다.일부 프로그래밍 언어 이론가는 익명 함수(함수 리터럴)에 [2]대한 지원도 요구합니다.1급 함수를 사용하는 언어에서는 함수 이름은 특별한 상태를 가지지 않으며 함수 [3]유형의 일반 변수처럼 취급됩니다.이 용어는 크리스토퍼 스트레이시에 의해 1960년대 [4]중반 "일류 시민으로서의 기능"이라는 맥락에서 만들어졌다.
1등급 함수는 고차 함수의 사용이 표준 관행인 함수 프로그래밍 스타일에 필수적입니다.상위 함수의 간단한 예로는 맵 함수를 들 수 있습니다. 맵 함수는 함수 및 목록을 인수로서 인수하여 목록의 각 멤버에 함수를 적용하여 작성된 목록을 반환합니다.언어가 맵을 지원하려면 인수로서의 함수 전달을 지원해야 합니다.
함수를 인수로 전달하거나 결과로 반환할 때, 특히 중첩된 함수와 익명 함수에 도입된 로컬 변수가 없는 경우 구현에 어려움이 있습니다.과거에는 이러한 문제를 funarg 문제라고 불렀는데, 이 이름은 "함수 인수"[5]에서 유래했습니다.초기 명령형 언어에서는 결과 유형으로서의 함수를 지원하지 않거나(예: ALGOL 60, Pascal), 중첩된 함수를 생략하여(예: C) 이러한 문제를 피했습니다.초기 함수 언어 Lisp는 동적 범위 지정 방식을 취했습니다. 여기서 로컬 변수가 아닌 변수는 정의된 위치가 아닌 함수가 실행되는 지점에서 변수의 가장 가까운 정의를 나타냅니다.어휘 범위 1종 함수에 대한 적절한 지원은 체계에 도입되었으며, 함수에 대한 참조를 베어 함수 [4]포인터 대신 폐쇄로 처리해야 하므로 가비지 수집이 필요합니다.
개념
이 섹션에서는 기능이 2등급 시민(C)인 명령어(commandative language)와 비교하여 기능어(first-class functions)에서 특정 프로그래밍 숙어가 어떻게 처리되는지를 비교한다.
고차 함수: 함수를 인수로 전달
함수가 퍼스트 클래스 시티즌인 언어에서는 함수를 다른 값과 같은 방법으로 다른 함수에 인수로서 전달할 수 있다(다른 함수를 인수로 하는 함수를 고차 함수라고 한다).Haskell 언어로:
지도 :: (a -> b) -> [a] -> [b] 지도 f [] = [] 지도 f (x:xs) = f x : 지도 f xs
함수가 퍼스트 클래스가 아닌 언어에서는 함수 포인터나 위임 등의 기능을 사용하여 고차 함수를 작성할 수 있습니다.언어 C:
무효 지도(인트 (*f)(인트), 인트 x[], size_t n) { 위해서 (인트 i = 0; i < > n; i++) x[i] = f(x[i]); }
두 접근법 사이에는 1등급 기능의 지원과 직접 관련이 없는 많은 차이가 있다.Haskell 샘플은 리스트에서 동작하고 C 샘플은 어레이에서 동작합니다.둘 다 해당 언어에서 가장 자연스러운 복합 데이터 구조이기 때문에 C 샘플이 링크된 목록에서 작동하도록 하는 것은 불필요하게 복잡해졌을 것입니다.이는 또한 C 함수에 추가 매개 변수가 필요하다는 사실도 설명합니다(배열 크기 제공).C 함수는 배열을 인플레이스 상태로 업데이트하고 값을 반환하지 않지만 Haskell의 경우 데이터 구조가 영구적입니다(이전 목록은 그대로 유지되고 새 목록은 반환됨).Haskell 표본은 재귀를 사용하여 목록을 이동하는 반면 C 표본은 반복을 사용합니다.이것은 두 언어 모두에서 이 함수를 표현하는 가장 자연스러운 방법이지만, Haskell 샘플은 접힘으로, C 샘플은 재귀로 쉽게 표현될 수 있었습니다.마지막으로, Haskell 함수는 다형성 유형을 가지며, 이는 C에서 지원되지 않기 때문에 우리는 모든 유형 변수를 유형 상수로 고정했습니다.int
.
어나니머스 함수 및 중첩 함수
익명 함수를 지원하는 언어에서는 인수와 같은 함수를 고차 함수에 전달할 수 있습니다.
주된 = 지도 (\x -> 3 * x + 1) [1, 2, 3, 4, 5]
익명 기능을 지원하지 않는 언어에서는 대신 이름으로 바인드해야 합니다.
인트 f(인트 x) { 돌아가다 3 * x + 1; } 인트 주된() { 인트 목록.[] = {1, 2, 3, 4, 5}; 지도(f, 목록., 5); }
비국소 변수 및 폐쇄
일단 익명 또는 중첩된 함수가 있으면 해당 함수가 신체 외부의 변수(비 로컬 변수라고 함)를 참조하는 것이 자연스러워집니다.
주된 = 허락하다 a = 3 b = 1 에 지도 (\x -> a * x + b) [1, 2, 3, 4, 5]
함수가 베어 함수 포인터로 표현되면 함수의 본문 밖에 있는 값이 어떻게 전달되어야 하는지 알 수 없으며, 따라서 닫힘을 수동으로 구축해야 합니다.따라서 여기서는 "일류" 기능에 대해 말할 수 없습니다.
유형화된 구조 { 인트 (*f)(인트, 인트, 인트); 인트 *a; 인트 *b; } 닫힘_t; 무효 지도(닫힘_t *닫힘, 인트 x[], size_t n) { 위해서 (인트 i = 0; i < > n; ++i) x[i] = (*닫힘->f)(*닫힘->a, *닫힘->b, x[i]); } 인트 f(인트 a, 인트 b, 인트 x) { 돌아가다 a * x + b; } 무효 주된() { 인트 l[] = {1, 2, 3, 4, 5}; 인트 a = 3; 인트 b = 1; 닫힘_t 닫힘 = {f, &a, &b}; 지도(&닫힘, l, 5); }
또,map
이제 2개의 기능에 특화되어 있습니다.int
환경 밖에 있습니다.이것은 보다 일반적으로 설정할 수 있지만 더 많은 보일러 플레이트 코드가 필요합니다.한다면f
네스트된 함수일 경우 동일한 문제가 발생할 수 있으며,[6] 이것이 C에서 지원되지 않는 이유입니다.
고차 함수: 함수가 결과로 반환됨
함수를 반환할 때 사실상 닫힘을 반환하는 것입니다.C 예제에서는 폐쇄를 작성하는 함수에서 돌아오면 폐쇄에 의해 캡처된 모든 로컬 변수가 범위를 벗어납니다.나중에 강제로 닫으면 정의되지 않은 동작이 발생하여 스택이 손상될 수 있습니다.이것은 상승 기능 문제로 알려져 있습니다.
변수에 함수 할당
변수에 함수를 할당하고 (글로벌) 데이터 구조 내에 저장하는 것은 함수를 반환하는 것과 같은 어려움을 겪을 수 있습니다.
f :: [[정수] -> [정수]] f = 허락하다 a = 3 b = 1 에 [지도 (\x -> a * x + b), 지도 (\x -> b * x + a)]
기능의 등가
대부분의 리터럴과 값을 동등하게 테스트할 수 있기 때문에 프로그래밍 언어가 동등하게 테스트 기능을 지원할 수 있는지 여부를 묻는 것은 자연스러운 일입니다.더 자세히 살펴보면, 이 질문은 더 어려워 보이며, 몇 가지 유형의 기능 [7]동일성을 구별해야 합니다.
- 확장적 평등
- 두 함수 f와 g는 모든 입력에 대한 출력에 일치하는 경우 확장적으로 동일한 것으로 간주됩니다(124x. f(x) = g(x)).예를 들어, 이 균등성의 정의에서는 삽입 정렬과 병합 정렬과 같은 안정적인 정렬 알고리즘의 두 가지 구현이 동등하다고 간주됩니다.확장적 동등성에 대한 결정은 일반적으로 결정되지 않으며, 한정된 도메인을 가진 함수의 경우에도 종종 다루기 어렵습니다.이러한 이유로 어떤 프로그래밍 언어도 확장적 평등과 같은 함수 평등을 구현하지 않습니다.
- 집중적 평등
- 집중 등식 하에서는 두 함수 f와 g가 동일한 "내부 구조"를 가진 경우 동일한 것으로 간주한다.이러한 종류의 동등성은 함수 본문의 소스 코드(Interprated Lisp 1.5 등) 또는 컴파일된 언어의 객체 코드를 비교함으로써 인터프리터 언어로 구현될 수 있습니다.인텐션 등식은 확장 등식을 의미합니다(함수가 결정론적이고 프로그램 카운터나 가변 글로벌 변수와 같은 숨겨진 입력이 없다고 가정합니다).
- 기준 등식
- 확장적 평등과 집중적 평등 구현의 비실용성을 고려할 때, 동등성을 위한 테스트 기능을 지원하는 대부분의 언어는 참조 동등성을 사용한다.모든 기능 또는 폐쇄에는 고유 식별자(일반적으로 기능 본체 또는 폐쇄의 주소)가 할당되며, 동일성은 식별자의 동일성에 기초하여 결정된다.별도로 정의되어 있지만 그렇지 않으면 동일한 함수 정의는 불평등한 것으로 간주됩니다.참조 평등은 집중적 평등과 확장적 평등을 의미한다.참조 등식은 참조 투명성을 깨뜨리기 때문에 해스켈과 같은 순수 언어에서는 지원되지 않습니다.
유형 이론
유형 이론에서, 유형 A의 값과 유형 B의 반환 값을 받아들이는 함수 유형은 A → B 또는A B로 기록될 수 있다.Curry-Howard 대응에서 함수 유형은 논리적 함의와 관련이 있다. 람다 추상화는 가정 가정 방출에 해당하며 함수 적용은 모드 포넨 추론 규칙에 해당한다.일반적인 프로그래밍 함수의 경우 외에도, 유형 이론은 연관 배열 및 유사한 데이터 구조를 모델링하기 위해 일급 함수를 사용합니다.
프로그래밍의 범주 이론 계정에서, 1등급 함수의 가용성은 닫힌 범주 가정에 해당합니다.예를 들어, 단순하게 유형화된 람다 미적분은 데카르트 닫힌 범주의 내부 언어에 해당합니다.
언어 지원
Erlang, Scheme, ML, Haskell, F# 및 Scala와 같은 기능 프로그래밍 언어에는 모두 퍼스트 클래스 함수가 있습니다.최초의 기능 언어 중 하나인 Lisp가 설계되었을 때, 일급 함수의 모든 측면이 제대로 이해되지 않았고, 그 결과 함수의 범위가 동적으로 지정되었습니다.최신 Scheme 및 Common Lisp 방언에는 사전 스코프의 퍼스트 클래스 함수가 있습니다.
Perl, Python, PHP, Lua, Tcl/Tk, JavaScript 및 Io를 포함한 많은 스크립트 언어들이 퍼스트 클래스 함수를 가지고 있습니다.
명령어는 알골과 그 후손인 파스칼, 전통적인 C 어족, 그리고 현대의 쓰레기 수집 변종들을 구별해야 한다.Algol 패밀리는 네스트된 함수 및 고차 취득 함수를 인수로 허용하지만 결과로 함수를 반환하는 고차 함수(이를 허용하는 Algol 68 제외)는 허용하지 않습니다.그 이유는 네스트된 함수가 결과적으로 반환된 경우(Algol 68은 이러한 경우 런타임 오류를 발생시키는 경우) 로컬 변수가 아닌 변수에 어떻게 대처해야 하는지 알 수 없었기 때문입니다.
C 패밀리는 함수를 인수로 전달하고 결과로 반환하는 것을 모두 허용했지만 중첩된 함수를 지원하지 않음으로써 문제를 회피했습니다.(gcc 컴파일러에서는 확장으로 사용할 수 있습니다).반환 함수의 유용성은 주로 최상위 함수 대신 로컬 변수가 아닌 중첩 함수를 반환하는 능력에 있기 때문에 이러한 언어는 일반적으로 1등급 함수를 갖는 것으로 간주되지 않습니다.
현대의 명령어는 종종 가비지 수집을 지원하므로 1종 함수의 구현이 가능합니다.퍼스트 클래스 함수는 C# 2.0과 C, C++ 및 Objective-C로의 Apple Blocks 확장을 포함한 언어의 최신 리비전에서만 지원되는 경우가 많습니다.C++11은 언어에 익명 함수 및 폐쇄에 대한 지원을 추가했지만, 언어의 비-쓰레기 수집 특성 때문에 함수의 비-로컬 변수가 결과로 반환되는 데 각별히 주의해야 합니다(아래 참조).
언어 | 고차 함수 | 중첩 함수 | 비로컬 변수 | 메모들 | ||||
---|---|---|---|---|---|---|---|---|
논쟁들 | 결과. | 이름 지어진 | 익명 | 폐쇄 | 부분적용 | |||
알골족 | 알골 60 | 네. | 아니요. | 네. | 아니요. | 아래 | 아니요. | 기능 타입이 있다. |
알골 68 | 네. | 네, 그렇습니다[8]. | 네. | 네. | 아래[9] | 아니요. | ||
파스칼 | 네. | 아니요. | 네. | 아니요. | 아래 | 아니요. | ||
아다 | 네. | 아니요. | 네. | 아니요. | 아래 | 아니요. | ||
오베론 | 네. | 비내스트만 | 네. | 아니요. | 아래 | 아니요. | ||
델파이 | 네. | 네. | 네. | 2009 | 2009 | 아니요. | ||
C패밀리 | C | 네. | 네. | GNU C에서는 있음 | Clang(블록)에 있음 | Clang(블록)에 있음 | 아니요. | 함수 포인터가 있습니다. |
C++ | 네. | 네. | C++11[10] | C++11[11] | C++11[11] | C++11 | 함수 포인터, 함수 오브젝트가 있습니다(아래 참조). 명시적 부분적 적용 가능 | |
C# | 네. | 네. | 7 | 2.0 / 3.0 | 2.0 | 3.0 | 딜러(2.0) 및 람다 식(3.0)이 있습니다. | |
목표-C | 네. | 네. | 익명 사용 | 2.0 + 블록[12] | 2.0 + 블록 | 아니요. | 함수 포인터가 있습니다. | |
자바 | 네. | 네. | 익명 사용 | 자바 8 | 자바 8 | 네. | 익명의 내부 클래스가 있습니다. | |
가세요 | 네. | 네. | 익명 사용 | 네. | 네. | 네, 그렇습니다[13]. | ||
림보 | 네. | 네. | 네. | 네. | 네. | 아니요. | ||
뉴스큐크 | 네. | 네. | 네. | 네. | 네. | 아니요. | ||
녹 | 네. | 네. | 네. | 네. | 네. | 네, 그렇습니다[14]. | ||
기능 언어 | 리스프 | 구문 | 구문 | 네. | 네. | 일반적인 리스프 | 아니요. | (아래 참조) |
스킴 | 네. | 네. | 네. | 네. | 네. | SRFI 26[15] | ||
줄리아. | 네. | 네. | 네. | 네. | 네. | 네. | ||
클로쥬르 | 네. | 네. | 네. | 네. | 네. | 네. | ||
ML | 네. | 네. | 네. | 네. | 네. | 네. | ||
하스켈 | 네. | 네. | 네. | 네. | 네. | 네. | ||
스칼라 | 네. | 네. | 네. | 네. | 네. | 네. | ||
얼랑 | 네. | 네. | 네. | 네. | 네. | 네. | ||
F# | 네. | 네. | 네. | 네. | 네. | 네. | ||
OCaml | 네. | 네. | 네. | 네. | 네. | 네. | ||
스크립트 언어 | 이오 | 네. | 네. | 네. | 네. | 네. | 아니요. | |
자바스크립트 | 네. | 네. | 네. | 네. | 네. | ECMAScript 5 | ES3 사용자 랜드 코드로 부분 적용 가능 | |
루아 | 네. | 네. | 네. | 네. | 네. | 네, 그렇습니다[17]. | ||
PHP | 네. | 네. | 익명 사용 | 5.3 | 5.3 | 아니요. | 사용자 랜드 코드로 부분 적용이 가능합니다. | |
펄 | 네. | 네. | 6 | 네. | 네. | 6개[18] | ||
파이썬 | 네. | 네. | 네. | 식만 | 네. | 2.5[19] | (아래 참조) | |
루비 | 구문 | 구문 | 비스코프 | 네. | 네. | 1.9 | (아래 참조) | |
기타 언어 | 포트란 | 네. | 네. | 네. | 아니요. | 아니요. | 아니요. | |
메이플 | 네. | 네. | 네. | 네. | 네. | 아니요. | ||
매스매티카 | 네. | 네. | 네. | 네. | 네. | 아니요. | ||
매트랩 | 네. | 네. | 네. | 네, 그렇습니다[20]. | 네. | 네. | 신기능 [21]자동생성으로 부분적 적용이 가능합니다. | |
스몰토크 | 네. | 네. | 네. | 네. | 네. | 부분적 | 도서관을 통해 부분 신청이 가능합니다. | |
재빠르다 | 네. | 네. | 네. | 네. | 네. | 네. |
- C++
- C++11 폐쇄는 복사 시공, 참조(수명 연장 없이) 또는 이동 시공(폐쇄 기간 동안 변수가 지속됨)에 의해 비국부 변수를 캡처할 수 있습니다.첫 번째 옵션은 닫힘이 반환되지만 복사가 필요하며 원래 변수를 수정하는 데 사용할 수 없는 경우 안전합니다(닫힘을 호출할 때 더 이상 존재하지 않을 수 있음).두 번째 옵션은 비용이 많이 드는 복사본을 잠재적으로 피하고 원래 변수를 수정할 수 있지만 닫힘이 반환되는 경우에는 안전하지 않습니다(당글링 참조 참조 참조).세 번째 옵션은 닫힘이 반환되어 복사가 필요하지 않지만 원래 변수를 수정하는 데 사용할 수 없는 경우 안전합니다.
- 자바
- Java 8 폐쇄는 최종 또는 "효과적인 최종" 비 로컬 변수만 캡처할 수 있습니다.Java의 함수 유형은 클래스로 표시됩니다.어나니머스 함수는 컨텍스트에서 유추된 유형을 취합니다.메서드 참조는 제한되어 있습니다.자세한 내용은 Anonymous 함수 » Java 제한을 참조하십시오.
- 리스프
- 어휘 범위 Lisp 변형은 폐쇄를 지원합니다.동적 범위 변형은 폐쇄를 지원하지 않거나 [22]폐쇄를 생성하기 위한 특수 구조가 필요하다.
- Common Lisp에서는 함수 이름 공간에 있는 함수의 식별자를 퍼스트 클래스 값에 대한 참조로 사용할 수 없습니다.특수 연산자
function
함수의 값을 취득하려면 , 를 사용할 필요가 있습니다.(function foo)
는 함수 객체로 평가됩니다.#'foo
는 약어 표기법으로 존재합니다.이러한 함수 개체를 적용하려면funcall
기능:(funcall #'foo bar baz)
. - 파이썬
- 명시적 부분 적용:
functools.partial
버전 2.5 이후 및operator.methodcaller
버전 2.6 이후입니다. - 루비
- Ruby의 일반 "함수" 식별자(실제 메서드)는 값으로 사용하거나 전달할 수 없습니다.먼저 이 명령어를
Method
또는Proc
1등급 데이터로 사용되는 객체입니다.이러한 함수 개체를 호출하는 구문은 일반 메서드를 호출하는 구문과 다릅니다. - 중첩된 메서드 정의는 실제로 범위를 중첩하지 않습니다.
- 와의 명시적 카레링
[1]
.
「 」를 참조해 주세요.
- 기능 해제
- 평가하다
- 퍼스트 클래스 메시지
- 카파 미적분 – 일급 함수를 제외한 형식주의
- 남자 또는 남자 테스트
- 부분적용
메모들
- ^ Abelson, Harold; Sussman, Gerald Jay (1984). Structure and Interpretation of Computer Programs. MIT Press. Formulating Abstractions with Higher-Order Procedures. ISBN 0-262-01077-1.
- ^ 프로그래밍 언어 실용론, 마이클 리 스콧, 섹션 11.2 "기능 프로그래밍"
- ^ Roberto Ierusalimschy; Luiz Henrique de Figueiredo; Waldemar Celes (2005). "The Implementation of Lua 5.0". Journal of Universal Computer Science. 11 (7): 1159–1176. doi:10.3217/jucs-011-07-1159.
- ^ a b Burstall, Rod; Strachey, Christopher (2000). "Understanding Programming Languages" (PDF). Higher-Order and Symbolic Computation. 13 (52): 11–49. doi:10.1023/A:1010052305354. S2CID 1989590. Archived from the original on February 16, 2010.
{{cite journal}}
: CS1 maint : bot : 원래 URL 상태 불명 (link) (2010-02-16 도) - ^ 조엘 모제스."LISP의 기능, 또는 FUNARG 문제를 환경 문제라고 부르는 이유"MIT AI 메모199, 1970.
- ^ "포함 함수가 종료된 후 주소를 통해 중첩 함수를 호출하려고 하면 모든 지옥이 해제됩니다." (GNU 컴파일러 컬렉션: 중첩된 함수)
- ^ 앤드류 W. 아펠(1995).지속에 대한 "강도의 평등;=").
- ^ Tanenbaum, A.S. (1977). "A comparison of PASCAL and Algol 68". The Computer Journal. 21 (4): 319. doi:10.1093/comjnl/21.4.316.
- ^ "The History of Python: Origins of Python's "Functional" Features". 21 April 2009.
- ^ 람다/클로저를 사용한 중첩 함수
- ^ a b 문서 번호 1968: V Samko; J Willcock, J Jérvi, D Gregor, A Lumsdaine (2006년 2월 26일) C++용 람다 표현 및 폐쇄
- ^ "Mac Dev Center: Blocks Programming Topics: Introduction". Archived from the original on 2009-08-31.
- ^ "2 examples in Go that you can have partial application".
- ^ "partial_application". Docs.rs. Retrieved 2020-11-03.
- ^ "SRFI 26: Notation for Specializing Parameters without Currying".
- ^ "John Resig - Partial Application in JavaScript".
- ^ Katz, Ian (July 23, 2010). "Lua Code for Curry (Currying Functions)". Archived from the original on 2018-11-06.
- ^ "Blog Perlgeek.de :: Currying".
- ^ "What's New in Python 2.5 — Python 3.10.0 documentation".
- ^ "Anonymous Functions - MATLAB & Simulink - MathWorks United Kingdom".
- ^ MATLAB의 부분 기능 평가
- ^ Wayback Machine에서 2012-03-19년 ZetaLisp 폐쇄 기록
레퍼런스
- 레오니다스 페가라스."기능 언어 및 고차 함수"CSE5317/CSE4305: 컴파일러 설계 및 구성알링턴에 있는 텍사스 대학교.
외부 링크
- 로제타 코드의 1등급 기능.
- IBM developerWorks의 고차 함수