고차 함수
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.
1. 개요
고차 함수는 함수를 인자로 받거나 결과로 반환하는 함수를 의미하며, 함수형 프로그래밍의 핵심 개념 중 하나이다. 이를 통해 코드 재사용성과 추상화 수준을 높일 수 있으며, 함수를 데이터처럼 취급할 수 있게 된다. 고차 함수의 예시로는 map, filter, reduce (fold) 등이 있으며, 함수형 프로그래밍 언어 뿐만 아니라 C++, 자바, 자바스크립트, 파이썬 등 다양한 프로그래밍 언어에서도 지원된다. 고차 함수를 지원하지 않는 언어에서는 함수 포인터, 매크로, 객체 등을 통해 유사한 기능을 구현할 수 있다.
더 읽어볼만한 페이지
- 고차 함수 - 커링
커링은 다수의 인수를 취하는 함수를 단일 인수를 취하는 함수들의 연속으로 변환하는 기법으로, 함수형 프로그래밍에서 다인수 함수를 분해하여 적용하는 방식이며, 논리학과 컴퓨터 과학에서 널리 활용되는 중요한 개념이다. - 고차 함수 - Map (고차 함수)
Map 고차 함수는 함수형 프로그래밍에서 컬렉션의 각 요소에 함수를 적용해 새로운 컬렉션을 생성하는 기능으로, Lisp에서 처음 소개된 후 다양한 언어에서 구현되어 요소별 연산을 간결하게 표현하며, 구현 방식과 기능은 언어별로 차이가 있다. - 람다 대수 - 익명 함수
익명 함수는 이름이 없는 함수로, 람다 추상, 람다 함수, 람다 표현식, 화살표 함수 등으로 불리며, 함수형 프로그래밍 언어에서 람다식 형태로 많이 사용되고 고차 함수의 인수, 클로저, 커링 등에 활용되지만, 재귀 호출의 어려움이나 기능 제한과 같은 단점도 존재한다. - 람다 대수 - 커링
커링은 다수의 인수를 취하는 함수를 단일 인수를 취하는 함수들의 연속으로 변환하는 기법으로, 함수형 프로그래밍에서 다인수 함수를 분해하여 적용하는 방식이며, 논리학과 컴퓨터 과학에서 널리 활용되는 중요한 개념이다. - 함수형 프로그래밍 - 패턴 매칭
패턴 매칭은 데이터 구조나 문자열에서 특정 패턴을 찾아 식별하는 기법으로, 다양한 프로그래밍 언어와 시스템에서 사용되며 데이터 필터링, 추출 및 선언적 프로그래밍에 중요한 역할을 수행한다. - 함수형 프로그래밍 - 익명 함수
익명 함수는 이름이 없는 함수로, 람다 추상, 람다 함수, 람다 표현식, 화살표 함수 등으로 불리며, 함수형 프로그래밍 언어에서 람다식 형태로 많이 사용되고 고차 함수의 인수, 클로저, 커링 등에 활용되지만, 재귀 호출의 어려움이나 기능 제한과 같은 단점도 존재한다.
| 고차 함수 | |
|---|---|
| 기본 정보 | |
| 이름 | 고차 함수 |
| 언어별 명칭 | 한국어: 고차 함수 영어: Higher-order function 일본어: 高階関数 (Kōkai kansū, 코카이 칸수) |
| 정의 | 다른 함수를 인자(argument)로 받거나, 함수를 결과로 반환하는 함수. 함수를 값으로 다루는 함수. |
| 특징 | |
| 활용 | 함수형 프로그래밍에서 중요한 역할 수행. 함수를 데이터처럼 다루어 코드의 유연성과 재사용성을 높임. |
| 예시 | 함수를 인자로 받는 함수: map, filter, reduce 함수를 반환하는 함수: 클로저를 생성하는 함수 |
| 장점 | 코드의 추상화 수준을 높여 가독성을 향상시킴. 함수 합성을 통해 복잡한 로직을 간결하게 표현 가능. 코드 재사용성을 높여 개발 생산성을 향상시킴. |
| 단점 | 함수 호출 오버헤드가 발생할 수 있음. 디버깅이 어려워질 수 있음. 익숙하지 않은 개발자에게는 코드 이해가 어려울 수 있음. |
| 예제 (JavaScript) | |
| 함수를 인자로 받는 함수 | 예시: map 함수 설명: 배열의 각 요소에 대해 주어진 함수를 적용하고, 새로운 배열을 반환. |
| 함수를 반환하는 함수 | 예시: 클로저를 생성하는 함수 설명: 특정 값을 기억하는 함수를 반환하여, 상태를 유지하는 데 사용. |
2. 역사적 배경
고차 함수의 개념은 람다 대수에서 유래되었다. 람다 대수는 함수를 값으로 취급하는 수학적 체계로, 함수형 프로그래밍 언어의 이론적 기반이 되었다.[2] 1950년대 후반에 등장한 LISP는 고차 함수를 지원하는 최초의 프로그래밍 언어 중 하나였으며, 이후 Scheme, ML, Haskell 등 다양한 함수형 언어들이 등장하면서 고차 함수는 프로그래밍의 중요한 개념으로 자리 잡았다.
고차 함수는 함수를 인자로 받거나 결과로 반환하는 함수이다. 이는 함수형 프로그래밍의 핵심 개념으로, 코드 재사용성과 추상화 수준을 높여준다. 함수를 데이터처럼 취급할 수 있게 해주는 것이다.[2][3]
3. 주요 특징
C나 파스칼에서는 함수 포인터를 이용하여 고차 함수를 흉내낼 수 있지만, 함수 포인터로 일급 함수를 지원한다고 보지는 않는다. 고차 함수는 주로 함수형 프로그래밍 언어나 그 배경 이론인 람다 계산법에서 많이 사용된다.
어떤 함수의 인수가 되는 함수는 '''함수 인수'''[2] 또는 '''절차 인수'''[3]라고 부르기도 한다.
3. 1. 함수를 인자로 받는 함수
함수를 인자로 받는 함수는 다른 함수를 인자로 받아 그 함수의 동작을 변경하거나 확장할 수 있다. 이러한 고차 함수의 대표적인 예시로는 map, filter, reduce (또는 `fold`) 함수가 있다.
C 표준 함수 `qsort`는 비교 함수를 매개변수로 받아, 프로그래머가 정렬 알고리즘을 정렬되는 항목의 비교와 분리할 수 있게 한다.
다음은 주어진 함수에 인자 2와 3을 전달하여 호출하고 반환값을 반환하는 고차 함수 `apply_2_3`의 예시이다.
(define (add x y) (+ x y))
(define (mul x y) (* x y))
(define (apply_2_3 f) (f 2 3))
(apply_2_3 add) ; => (add 2 3) => 5
(apply_2_3 mul) ; => (mul 2 3) => 6
이 외에도 apply, 함수 합성, 적분, 콜백, 트리 순회, 몬테규 문법 등에서 고차 함수가 활용된다.
3. 1. 1. map 함수
map 함수는 함수 ''f''와 요소들의 모음을 인자로 받아, 모음의 각 요소에 ''f''를 적용한 새로운 모음을 결과로 반환하는 고차 함수의 한 예시이다. 리스트 구조의 각 요소에 대해 순차적으로 주어진 함수를 처리하며, 함수형 프로그래밍이 아닌 패러다임의 언어에서도 구현되어 있는 경우가 있다.
예를 들어, 리스트 '(1 2 3)의 각 요소에 1을 더한 리스트를 얻고 싶을 경우 다음과 같이 할 수 있다.
```scheme
(map (lambda (n) (+ 1 n)) '(1 2 3))
;=> '(2 3 4)
3. 1. 2. filter 함수
필터는 컬렉션의 각 요소에 대해 주어진 조건(함수)을 만족하는 요소만 추출하여 새로운 컬렉션을 반환한다.
리스트의 각 요소를 각각 인수로 전달하여 인수로 전달된 함수를 호출하고, 그 반환값이 참이면 반환값 리스트에 남기고, 거짓이면 제외한다. 제외하고 싶은 요소에 대해 거짓 값을 반환하는, 술어와 같은 역할을 하는 함수를 주어 사용한다.[4]
예를 들어, 리스트 '(1 2 -3 -4 5)에서 양수만 추출하려면 다음과 같이 한다.[4]
```scheme
(filter positive? '(1 2 -3 -4 5))
;=> '(1 2 5)
3. 1. 3. reduce (fold) 함수
고차 함수의 일종인 ''fold'' 함수는 중첩, 누산, 겹침, 접기 등으로 불리며, 영어로는 ''reduce'' 또는 ''inject''라고도 한다. fold는 리스트의 각 요소에 주어진 함수를 적용하여 하나의 값을 만든다.
```scheme
(fold func returns-first list)
```
예를 들어, 리스트 '(1 2 3)의 각 요소의 총합을 구하려면 다음과 같이 할 수 있다.
```scheme
(fold + 0 '(1 2 3))
;=> 6
```
오른쪽 방향 겹침(''foldr'')과 왼쪽 방향 겹침(''foldl'')이 있으며, 프로그래밍 언어에 따라 처음부터 유사한 기능이 내장되어 있는 경우도 있다. (자세한 내용은 fold의 영어판 기사 참조)
```scheme
(fold-right + 0 '(1 2 3))
;=> 6
; (+ 1 (+ 2 (+ 3 0)))과 동일
(fold-right cons '(0) '(1 2 3))
;=> '(1 2 3 0)
; (cons 1 (cons 2 (cons 3 '(0))))과 동일
```
```scheme
(fold-left + 0 '(1 2 3))
;=> 6
;(+ (+ (+ 0 1) 2 ) 3) 과 동일
(fold-left cons '(0) '(1 2 3))
;=> '((((0) . 1) . 2) . 3)
;(cons (cons (cons '(0) 1) 2 ) 3) 과 동일
3. 2. 함수를 반환하는 함수
함수를 반환하는 함수는 특정 동작을 수행하는 함수를 동적으로 생성하는 데 사용될 수 있다. 이러한 방식은 클로저와 함께 사용될 때 특히 유용하다.
고차 함수 `twice`는 함수를 인자로 받아, 어떤 값에 해당 함수를 두 번 적용한다. 만약 `twice`를 같은 함수 `f`에 대해 여러 번 적용해야 한다면, 값을 받는 대신 함수를 반환하는 것이 더 효율적이다. 이는 "반복 금지" 원칙을 따르는 방법이다.
3. 2. 1. 커링 (Currying)
커링은 여러 인수를 받는 함수를 단일 인수 함수로 변환하는 기법이다. 예를 들어 두 숫자를 더하는 함수 `add`는 다음과 같이 표현할 수 있다.[1]
일반적인 정의와 호출 방식은 다음과 같다.
```scheme
; 일반적인 정의, 호출
(define (add x y) (+ x y))
(add 2 3) ;=> 5
```
커링을 적용하면 다음과 같이 정의하고 호출할 수 있다.
```scheme
; 커링을 적용한 정의, 호출
(define (add x) (lambda (y) (+ x y)))
((add 2) 3) ;=> 5
```
일부 프로그래밍 언어는 내장 함수가 처음부터 커링되어 있다. 이는 함수 호출이 항상 1개의 인수를 받는 함수를 반환하도록 정의하는 것과 같다. 즉, n개의 인수를 받는 함수를 n개 인수의 직곱을 받는 함수로 정의하는 대신, 1개의 인수를 받는 고차 함수를 n개 나열한 것과 같은 형식으로 정의한다.[1]
예를 들어 하스켈에서는 다음과 같이 표현할 수 있다.[1]
```haskell
(2 +)
```
이는 앞서의 `(add 2)`와 마찬가지로, 2를 더하는 함수가 된다. `+` 함수가 이미 커링되어 있기 때문에, 1개의 인수를 적용하는 것만으로 함수로서 작동한다.[1] 다음은 실행 예시다.
```haskell
Prelude> 2 + 3
5
Prelude> (2 +) 3
5
4. 프로그래밍 언어별 지원 현황
다양한 프로그래밍 언어에서 고차 함수를 지원하며, 각 언어마다 구현 방식과 특징이 조금씩 다르다.
- 많은 함수형 프로그래밍 언어에서 발견되는 `map` 함수는 고차 함수의 한 예시이다.
- 정렬 함수는 비교 함수를 매개변수로 받아, 프로그래머가 정렬 알고리즘을 정렬되는 항목의 비교와 분리할 수 있게 한다. C 표준 함수 `qsort`가 그 예이다.[1]
- 필터[4]
- 접기
- apply
- 함수 합성
- 적분
- 콜백
- 트리 순회
- 자연어의 의미론적 이론인 몬테규 문법은 고차 함수를 사용한다.
함수형 프로그래밍 언어가 아니더라도 고차 함수와 같은 처리를 할 수 있는 경우가 있다. C나 Fortran의 함수 포인터 등이 그 예시이다. 이는 언어에 커리(Curry)화 기능이나 클로저 기능이 없다는 제약이 있다. C#, C++, Tcl 등 함수형 프로그래밍 언어가 아니더라도 람다식을 지원하는 언어는 많다.[1]
4. 1. 함수형 언어
함수형 프로그래밍 언어에서map 함수는 고차 함수의 한 예시이다. 이 함수는 함수 ''f''와 요소들의 모음을 인자로 받아, 모음의 각 요소에 ''f''를 적용한 새로운 모음을 결과로 반환한다.| 언어 | 코드 예시 |
|---|---|
| F# | |
| 하스켈 | |
| Scheme | |
| 클로저 | |
| Elixir | |
| 커먼 리스프 |
4. 2. 비함수형 언어
C, C++, 자바, 파이썬, 자바스크립트 등은 함수형 프로그래밍 언어가 아님에도 불구하고 고차 함수와 유사한 기능을 지원하여 함수형 프로그래밍의 이점을 활용할 수 있도록 한다. 예를 들어 C에서는 함수 포인터를 이용하여 함수를 인수로 받는 함수를 작성할 수 있다.```c
#include
/* int의 이항 연산 */
typedef int (*Function)(int, int);
/* 왼쪽 결합 */
int foldl(int values[], int length, Function f, int identity_element) {
int folded = identity_element;
for (int i = 0; i < length; i++) {
folded = (*f)(folded, values[i]);
}
return folded;
}
/* 오른쪽 결합 */
int foldr(int values[], int length, Function f, int identity_element) {
int folded = identity_element;
for (int i = length - 1; 0 <= i; i--) {
folded = (*f)(values[i], folded);
}
return folded;
}
/* 덧셈 */
int add(int x, int y) { return x + y; }
/* 곱셈 */
int mul(int x, int y) { return x * y; }
/* 사용 예시 */
int main(void) {
int values[5] = {31, 4, 159, 26, 53};
int sum = foldl(values, sizeof values / sizeof(int), add, 0);
int product = foldl(values, sizeof values / sizeof(int), mul, 1);
assert(sum == 273);
assert(product == 27168648);
return 0;
}
```
하지만 C에서는 함수를 반환값으로 하는 함수를 작성하기 어렵기 때문에 클로저를 고차 함수에 전달하여 편리하게 사용하기는 어렵다.
4. 2. 1. 파이썬
파이썬은 람다 표현식과 `map`, `filter`, `reduce` 등의 내장 함수를 통해 고차 함수를 지원한다.파이썬의 데코레이터 구문은 종종 함수를 고차 함수를 통과시킨 결과로 대체하는 데 사용된다. 예를 들어, 아래의 함수 `g`는 데코레이터를 사용한 예시이다.
>>> @twice
... def g(i):
... return i + 3
>>> g(7)
13
아래는 scheme 언어에서 `filter` 함수의 사용 예시이다. 리스트의 각 요소를 각각 인수로 전달하여 인수로 전달된 함수를 호출하고, 그 반환값이 참이면 반환값 리스트에 남기고, 거짓이면 제외한다.
```scheme
(filter func list1)
```
예를 들어, 리스트 '(1 2 -3 -4 5)에서 양수만 추출하려면 다음과 같이 한다.[4]
```scheme
(filter positive? '(1 2 -3 -4 5))
;=> '(1 2 5)
4. 2. 2. 자바
Java 8부터 람다 표현식과 함수형 인터페이스를 도입하여 고차 함수를 지원한다.[1]함수형 인터페이스를 사용한 예시는 다음과 같다.
```java
import java.util.function.*;
class Main {
public static void main(String[] args) {
Function
IntUnaryOperator plusThree = i -> i + 3;
var g = twice.apply(plusThree);
System.out.println(g.applyAsInt(7)); // 13
}
}
```
다음은 정적 메서드를 사용한 동일한 표현이다.
```java
import java.util.function.*;
class Main {
private static IntUnaryOperator twice(IntUnaryOperator f) {
return f.andThen(f);
}
private static int plusThree(int i) {
return i + 3;
}
public static void main(String[] args) {
var g = twice(Main::plusThree);
System.out.println(g.applyAsInt(7)); // 13
}
}
4. 2. 3. 자바스크립트
자바스크립트는 함수를 일급 객체로 취급하며, 람다 표현식(화살표 함수)과 `map`, `filter`, `reduce` 등의 배열 메서드를 통해 고차 함수를 지원한다.[1]다음은 화살표 함수를 사용한 고차 함수 예시이다.
"use strict";
const twice = f => x => f(f(x));
const plusThree = i => i + 3;
const g = twice(plusThree);
console.log(g(7)); // 13
다음은 기존 구문을 사용한 고차 함수 예시이다.
"use strict";
function twice(f) {
return function (x) {
return f(f(x));
};
}
function plusThree(i) {
return i + 3;
}
const g = twice(plusThree);
console.log(g(7)); // 13
위 예시에서 `twice` 함수는 함수 `f`를 인자로 받아, 함수 `f`를 두 번 적용하는 새로운 함수를 반환하는 고차 함수이다. `plusThree`는 숫자에 3을 더하는 함수이다. `g`는 `twice`에 `plusThree`를 적용하여 얻어진 함수로, 입력값에 3을 두 번 더하는 함수가 된다. 따라서 `g(7)`은 13을 반환한다.[1]
4. 2. 4. C++
C++11부터 람다 표현식과 `std::function`을 통해 고차 함수를 지원한다.`std::function` 사용 예시는 다음과 같다.
#include
#include
auto twice = [](const std::function
{
return [f](int x) {
return f(f(x));
};
};
auto plus_three = [](int i)
{
return i + 3;
};
int main()
{
auto g = twice(plus_three);
std::cout << g(7) << '\n'; // 13
}
C++14에서 제공되는 제네릭 람다 사용 예시는 다음과 같다.
#include
auto twice = [](const auto& f)
{
return [f](int x) {
return f(f(x));
};
};
auto plus_three = [](int i)
{
return i + 3;
};
int main()
{
auto g = twice(plus_three);
std::cout << g(7) << '\n'; // 13
}
5. 대안
함수 포인터는 C, C++, 포트란, 파스칼과 같은 언어에서 함수에 대한 참조를 전달할 수 있게 해준다. 다음 C 코드는 임의의 함수의 적분 근사값을 계산한다.[1]
```c
#include
double square(double x)
{
return x * x;
}
double cube(double x)
{
return x * x * x;
}
/* f()의 [a,b] 구간에서의 적분을 계산 */
double integral(double f(double x), double a, double b, int n)
{
int i;
double sum = 0;
double dt = (b - a) / n;
for (i = 0; i < n; ++i) {
sum += f(a + (i + 0.5) * dt);
}
return sum * dt;
}
int main()
{
printf("%g\n", integral(square, 0, 1, 100));
printf("%g\n", integral(cube, 0, 1, 100));
return 0;
}
```
C 표준 라이브러리의 qsort 함수는 고차 함수의 동작을 에뮬레이션하기 위해 함수 포인터를 사용한다.[1]
매크로는 고차 함수의 일부 효과를 얻는 데에도 사용될 수 있다. 하지만, 매크로는 변수 포획 문제를 쉽게 피할 수 없으며, 많은 양의 중복된 코드를 생성할 수 있는데, 이는 컴파일러가 최적화하기 더 어려울 수 있다. 매크로는 일반적으로 강력한 유형을 가지지 않지만, 강력한 유형의 코드를 생성할 수 있다.[1]
다른 명령형 프로그래밍 언어에서는 고차 함수를 통해 얻는 것과 동일한 알고리즘 결과를 평가 범위 내에서 코드를 동적으로 실행(때로는 ''Eval'' 또는 ''Execute'' 연산이라고 함)하여 달성할 수 있다. 이 접근 방식에는 다음과 같은 상당한 단점이 있을 수 있다.[1]
고차 함수를 지원하지 않는 객체 지향 프로그래밍 언어에서는 객체가 효과적인 대체 수단이 될 수 있다. 객체의 메서드는 본질적으로 함수처럼 작동하며, 메서드는 객체를 매개변수로 받아 객체를 반환 값으로 생성할 수 있다. 그러나 객체는 순수 함수에 비해 실행 시간 오버헤드가 더 클 수 있으며, 객체와 해당 메서드를 정의하고 인스턴스화하기 위한 보일러플레이트 코드가 추가될 수 있다. 스택 기반 메모리 할당(동적 메모리 할당 대신) 객체 또는 구조체를 허용하는 언어는 이 방법에 더 많은 유연성을 제공할 수 있다.[1]
다음은 Free Pascal에서 함수를 반환하는 함수와 함께 간단한 스택 기반 레코드를 사용하는 예제이다.[1]
```pascal
program example;
type
int = integer;
Txy = record x, y: int; end;
Tf = function (xy: Txy): int;
function f(xy: Txy): int;
begin
Result := xy.y + xy.x;
end;
function g(func: Tf): Tf;
begin
result := func;
end;
var
a: Tf;
xy: Txy = (x: 3; y: 7);
begin
a := g(@f); // return a function to "a"
writeln(a(xy)); // prints 10
end.
```
함수 `a()`는 `Txy` 레코드를 입력으로 받아 레코드의 `x` 및 `y` 필드의 합인 정수 값(3 + 7)을 반환한다.[1]
디펑셔널라이제이션은 일급 함수가 없는 언어에서 고차 함수를 구현하는 데 사용될 수 있다.[1]
```cpp
// 디펑셔널라이제이션된 함수 자료 구조
template
template
template
// 디펑셔널라이제이션된 함수 적용 구현
template
auto apply(Composition
return apply(f.f, apply(f.g, arg));
}
template
auto apply(Add
return arg + f.value;
}
template
auto apply(DivBy
return arg / f.value;
}
// 고차 합성 함수
template
Composition
return Composition
}
int main(int argc, const char* argv[]) {
auto f = compose(DivBy
apply(f, 3); // 4.0f
apply(f, 9); // 7.0f
return 0;
}
```
이 경우, 서로 다른 유형이 함수 오버로딩을 통해 다른 함수를 트리거하는 데 사용된다. 이 예제에서 오버로드된 함수의 시그니처는 `auto apply`이다.[1]
참조
[1]
웹사이트
PHP: Arrow Functions - Manual
https://www.php.net/[...]
2021-03-01
[2]
문서
functional argument/parameter
[3]
문서
procedural argument/parameter
[4]
웹사이트
http://www.shido.inf[...]
[5]
웹사이트
Functional Programming - Implementing Scan (Prefix Sum) using Fold
http://stackoverflow[...]
2014-09-14
[6]
뉴스
로드맵수학학원 - 수학 공부의 틀을 바꾸는 종횡무진 학습법
http://www.naeil.com[...]
내일신문
2014-10-17
[7]
뉴스
PART 01. 의심스러운 토대 위에 싹트다
http://www.dongascie[...]
동아사이언스
2015-08-27
[8]
뉴스
함수 언어 : 3대 함수 언어 전문가가 말하는 정체와 역할
http://www.itworld.c[...]
ITWorld
2016-02-19
본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.
문의하기 : help@durumis.com