함수(F#)

함수는 어떠한 프로그래밍 언어에서든 프로그램을 실행하는 데 있어 가장 기본이 되는 단위입니다.다른 언어에서와 마찬가지로 F# 함수에는 이름과 본문이 있으며 함수에 매개 변수와 인수를 사용할 수 있습니다.F#에서는 함수를 값으로 취급하고, 명명되지 않은 함수를 식에 사용하고, 함수를 합성하여 새 함수를 만들고, 함수를 변환하고, 함수 인수의 부분 적용을 통해 함수를 암시적으로 정의하는 등과 같은 함수형 프로그래밍 구문도 지원합니다.

함수를 정의하는 데는 let 키워드를 사용할 수도 있고, 함수가 재귀적인 경우 let rec 키워드 조합을 사용할 수도 있습니다.

// Non-recursive function definition.
let [inline] function-name parameter-list [ : return-type ] = function-body
// Recursive function definition.
let rec function-name parameter-list = recursive-function-body

설명

function-name은 함수를 나타내는 식별자입니다.parameter-list는 공백으로 구분된 일련의 매개 변수로 이루어집니다.매개 변수 단원에서 설명하는 것처럼 각 매개 변수에 대해 명시적 형식을 지정할 수 있습니다.특정 인수 형식을 지정하지 않으면 컴파일러에서 함수 본문을 기준으로 형식을 유추하려 시도합니다.function-body는 식으로 이루어집니다.함수 본문을 구성하는 식은 일반적으로 여러 개의 식으로 이루어진 복합 식입니다. 복합 식을 이루는 여러 식이 누적되어 반환 값인 최종 식을 얻습니다.return-type은 콜론 뒤에 형식이 옵니다. 이는 선택적 요소입니다.반환 값의 형식을 명시적으로 지정하지 않으면 컴파일러에서 최종 식을 기준으로 반환 형식을 결정합니다.

아래에는 간단한 함수 정의의 예가 나와 있습니다.

let f x = x + 1

위 예제에서 함수 이름은 f이고, 인수는 x이며, 인수의 형식은 int입니다. 함수 본문은 x + 1이고 반환 값의 형식은 int입니다.

인라인 지정자가 사용되는 경우 컴파일러에서는 이를 힌트로 삼아 함수의 크기가 작으며 함수의 코드를 호출자의 본문에 통합할 수 있는 것으로 판단합니다.

범위

모듈 범위 이외의 다른 범위에서 그 수준에 상관없이 값이나 함수 이름을 다시 사용할 수 있습니다.이름을 다시 사용하는 경우 앞서 선언한 이름 대신 나중에 선언한 이름이 사용됩니다.그러나 모듈의 최상위 범위에서는 이름이 중복되지 않아야 합니다.예를 들어 다음 코드가 모듈 범위에 있으면 오류가 발생하지만 함수 내에 있으면 오류가 발생하지 않습니다.

let list1 = [ 1; 2; 3]
// Error: duplicate definition.
let list1 = []  
let function1 =
   let list1 = [1; 2; 3]
   let list1 = []
   list1

그러나 다음 코드는 어떤 수준의 범위에서든 사용할 수 있습니다.

let list1 = [ 1; 2; 3]
let sumPlus x =
// OK: inner list1 hides the outer list1.
   let list1 = [1; 5; 10]  
   x + List.sum list1

매개 변수

매개 변수의 이름은 함수 이름 뒤에 나열됩니다.다음 예제에서와 같이 매개 변수의 형식을 지정할 수 있습니다.

let f (x : int) = x + 1

형식을 지정하는 경우 형식이 매개 변수의 이름 뒤에 와야 하고 이름과 형식을 콜론으로 구분해야 합니다.매개 변수의 형식을 생략하는 경우 컴파일러를 통해 매개 변수 형식이 유추됩니다.예를 들어 다음 함수 정의에서 인수 x는 int 형식인 것으로 유추됩니다. 1이 int 형식이기 때문입니다.

let f x = x + 1

그러나 컴파일러에서는 함수를 가능한 한 제네릭으로 만들려고 시도합니다.예를 들어, 다음 코드를 참조하십시오.

let f x = (x, x)

이 함수는 형식에 상관없이 인수 한 개를 취하여 튜플을 만듭니다.형식이 지정되어 있지 않으므로 함수에 사용할 수 있는 인수 형식에 제한이 없습니다.자세한 내용은 자동 일반화(F#)을 참조하십시오.

함수 본문

함수 본문에는 로컬 변수와 함수의 정의가 포함될 수 있습니다.이와 같은 변수 및 함수는 현재 함수의 본문 범위에 포함되며 그 범위를 벗어나지 않습니다.간단한 구문 옵션을 사용하는 경우에는 다음 예제에서와 같이 정의가 함수 본문에 포함되어 있음을 표시하기 위해 들여쓰기를 사용해야 합니다.

let cylinderVolume radius length =
    // Define a local value pi.
    let pi = 3.14159
    length * pi * radius * radius

자세한 내용은 코드 서식 지정 지침(F#)자세한 구문(F#)을 참조하십시오.

반환 값

컴파일러에서는 함수 본문의 최종 식을 사용하여 반환 값과 형식을 결정합니다.컴파일러는 이전 식을 기준으로 하여 최종 식의 형식을 유추할 수 있습니다.이전 단원에 나와 있는 함수 cylinderVolume에서 pi의 형식은 리터럴 3.14159 의 형식에 근거하여 float로 결정됩니다.컴파일러에서는 pi 형식을 사용하여 h * pi * r * r 식의 형식이 float인 것으로 결정합니다.따라서 이 함수의 전체 반환 형식은 float가 됩니다.

반환 값을 명시적으로 지정하려면 다음과 같이 코드를 작성해야 합니다.


let cylinderVolume radius length : float =
   // Define a local value pi.
   let pi = 3.14159
   length * pi * radius * radius

코드를 위와 같이 작성하면 컴파일러에서 float를 전체 함수에 적용합니다. 이를 매개 변수 형식에도 적용하고 싶으면 다음과 같은 코드를 사용합니다.

let cylinderVolume (radius : float) (length : float) : float

함수 호출

함수를 호출하려면 함수 이름을 지정하고 그 뒤에 공백을 추가한 다음 공백으로 구분된 인수를 필요한 만큼 추가합니다.예를 들어 cylinderVolume 함수를 호출하고 그 결과를 vol 값에 할당하려면 다음과 같은 코드를 작성합니다.

let vol = cylinderVolume 2.0 3.0

인수 부분 적용

지정된 수보다 적은 수의 인수를 제공하면 나머지 인수를 필요로 하는 새 함수가 만들어집니다.이와 같이 인수를 처리하는 방법을 변환이라고 하며 이는 F# 같은 함수형 프로그래밍 언어의 특징입니다.예를 들어 크기가 두 가지인 파이프를 사용하여 작업하는 경우를 가정해 봅시다. 그중 하나의 반지름은 2.0이고 다른 하나의 반지름은 3.0입니다.다음과 같이 파이프의 용적을 구하는 함수를 만들 수 있습니다.

let smallPipeRadius = 2.0
let bigPipeRadius = 3.0

// These define functions that take the length as a remaining
// argument:

let smallPipeVolume = cylinderVolume smallPipeRadius
let bigPipeVolume = cylinderVolume bigPipeRadius

그런 다음 서로 다른 두 가지 크기의 파이프에 대한 여러 가지 길이를 나타내는 데 필요한 인수를 추가로 제공할 수 있습니다.

let length1 = 30.0
let length2 = 40.0
let smallPipeVol1 = smallPipeVolume length1
let smallPipeVol2 = smallPipeVolume length2
let bigPipeVol1 = bigPipeVolume length1
let bigPipeVol2 = bigPipeVolume length2

재귀 함수

재귀 함수는 자기 자신을 호출하는 함수입니다.재귀 함수를 사용하려면 rec 키워드를 let 키워드 다음에 지정해야 합니다.함수 본문 내에서 재귀 함수를 호출하는 방법은 다른 함수를 호출하는 방법과 같습니다.아래 나와 있는 재귀 함수는 n번째 피보나치 수를 계산합니다.피보나치 수열은 아주 오래 전부터 알려져 있는 수열입니다. 이는 이어지는 각 값이 바로 앞 숫자 두 개를 더한 값과 같은 수열입니다.

let rec fib n = if n < 2 then 1 else fib (n - 1) + fib (n - 2)

누적기와 연속 문자의 사용 같은 특수 기술을 잘 이해하고 이를 고려하여 재귀 함수를 작성하지 않는 경우 재귀 함수에 따라 프로그램 스택의 오버플로가 발생하거나 작업 효율성이 떨어질 수 있습니다.

함수 값

F#의 모든 함수는 값으로 간주됩니다. 사실상 이를 함수 값이라고 합니다.함수는 값이므로 이를 다른 함수에 대한 인수로 사용하거나 값이 필요한 기타 컨텍스트에 사용할 수 있습니다.다음은 함수 값을 인수로 취하는 함수의 예입니다.

let apply1 (transform : int -> int ) y = transform y

함수 값의 형식을 지정하는 데는 -> 토큰을 사용합니다.이 토큰의 왼쪽에는 인수의 형식이, 오른쪽에는 반환 값이 배치됩니다.위 예제에서 apply1은 함수 transform을 인수로 취하는 함수입니다. 여기서 transform은 정수를 취하여 다른 정수를 반환하는 함수입니다.다음 코드에서는 apply1을 사용하는 방법을 보여 줍니다.

let increment x = x + 1

let result1 = apply1 increment 100

위 코드를 실행했을 때 result의 값은 101이 됩니다.

인수가 여러 개이면 다음 예제에서와 같이 연속적인 -> 토큰을 사용하여 각 인수를 구분할 수 있습니다.

let apply2 ( f: int -> int -> int) x y = f x y

let mul x y = x * y

let result2 = apply2 mul 10 20

결과는 200입니다.

람다 식

람다 식은 명명되지 않은 함수입니다.위 예제에서 명명된 함수 increment와 mul을 정의하는 대신 다음과 같이 람다 식을 사용할 수 있습니다.

let result3 = apply1 (fun x -> x + 1) 100

let result4 = apply2 (fun x y -> x * y ) 10 20

람다 식을 정의하는 데는 fun 키워드를 사용합니다.람다 식은 함수 정의와 비슷하지만 = 토큰 대신 -> 토큰을 사용하여 인수 목록을 함수 본문으로부터 구분한다는 점에서 차이가 있습니다.일반 함수 정의에서와 마찬가지로 인수 형식을 유추하거나 명시적으로 지정할 수 있습니다. 람다 식의 반환 형식은 본문에 있는 마지막 식의 형식을 기준으로 유추됩니다.자세한 내용은 람다 식: fun 키워드(F#)을 참조하십시오.

함수 컴퍼지션 및 파이프라인

F#에서 다른 함수를 사용하여 함수를 합성할 수 있습니다.function1과 function2라는 두 개의 함수를 합성하면 function1을 적용한 뒤에 function2를 적용하는 다른 함수가 만들어집니다.

let function1 x = x + 1
let function2 x = x * 2
let h = function1 >> function2
let result5 = h 100

결과는 202입니다.

파이프라인을 사용하면 함수 호출을 연속적인 작업으로 함께 연결할 수 있습니다.파이프라인은 다음과 같이 작동합니다.

let result = 100 |> function1 |> function2

이번에도 결과는 202입니다.

컴퍼지션 연산자 두 함수를 사용 하 고 반환 하는 함수. 반면, 파이프라인 연산자 함수와 인수를 사용 하 고 값을 반환 합니다.다음 코드 예제에서는 함수 시그니처 및 사용량의 차이 표시 하 여 파이프라인 및 컴퍼지션 연산자의 차이점을 보여 줍니다.

// Function composition and pipeline operators compared.

let addOne x = x + 1
let timesTwo x = 2 * x

// Composition operator
// ( >> ) : ('T1 -> 'T2) -> ('T2 -> 'T3) -> 'T1 -> 'T3
let Compose2 = addOne >> timesTwo

// Backward composition operator
// ( << ) : ('T2 -> 'T3) -> ('T1 -> 'T2) -> 'T1 -> 'T3
let Compose1 = addOne << timesTwo

// Result is 5
let result1 = Compose1 2

// Result is 6
let result2 = Compose2 2

// Pipelining
// Pipeline operator
// ( <| ) : ('T -> 'U) -> 'T -> 'U
let Pipeline1 x = addOne <| timesTwo x

// Backward pipeline operator
// ( |> ) : 'T1 -> ('T1 -> 'U) -> 'U
let Pipeline2 x = addOne x |> timesTwo

// Result is 5
let result3 = Pipeline1 2

// Result is 6
let result4 = Pipeline2 2

함수 오버 로딩

형식이 있지만 함수가 아니라 메서드를 오버 로드할 수 있습니다.자세한 내용은 메서드(F#)을 참조하십시오.

참고 항목

기타 리소스

값(F#)

F# 언어 참조