수색…
둘 이상의 매개 변수의 함수
F #에서는 모든 함수가 정확히 하나의 매개 변수를 사용 합니다. 이는 함수 선언에서 하나 이상의 매개 변수를 선언하는 것이 쉽기 때문에 이상한 진술로 보입니다.
let add x y = x + y
하지만 F # 인터프리터 인터프리터에 해당 함수 선언을 입력하면 형식 서명이 다음과 같이 표시됩니다.
val add : x:int -> y:int -> int
매개 변수 이름이 없으면 해당 서명은 int -> int -> int
입니다. ->
연산자는 오른쪽 연관입니다. 즉,이 서명은 int -> (int -> int)
. 즉, add
하나 개받는 함수이다 int
매개 변수를 하나 개받는 함수 반환 int
반환은 int
. 시도 해봐:
let addTwo = add 2
// val addTwo : (int -> int)
let five = addTwo 3
// val five : int = 5
그러나 add
와 같은 함수를 "기존"방식으로 호출하여 직접 두 개의 매개 변수를 전달할 수 있으며 예상대로 작동합니다.
let three = add 1 2
// val three : int = 3
이는 원하는만큼의 매개 변수가있는 함수에 적용됩니다.
let addFiveNumbers a b c d e = a + b + c + d + e
// val addFiveNumbers : a:int -> b:int -> c:int -> d:int -> e:int -> int
let addFourNumbers = addFiveNumbers 0
// val addFourNumbers : (int -> int -> int -> int -> int)
let addThreeNumbers = addFourNumbers 0
// val addThreeNumbers : (int -> int -> int -> int)
let addTwoNumbers = addThreeNumbers 0
// val addTwoNumbers : (int -> int -> int)
let six = addThreeNumbers 1 2 3 // This will calculate 0 + 0 + 1 + 2 + 3
// val six : int = 6
다중 매개 변수 함수를 하나의 매개 변수를 사용하여 새 함수를 반환하는 함수로 생각하는이 방법은 (최종 매개 변수를 사용하여 최종 결과를 반환하는 최종 함수에 도달 할 때까지 하나의 매개 변수를 사용하고 새 함수를 반환 할 수 있습니다) 개념을 개발하는 것으로 유명한 수학자 Haskell Curry에게 경의를 표하여 currying 이라고합니다. (다른 누군가에 의해 발명되었지만 카레 당은 당연히 대부분의 크레딧을 얻습니다.)
이 개념은 F #에서 사용되며, 익숙해지기를 원할 것입니다.
기능의 기본
F #의 대부분의 함수는 let
구문으로 생성됩니다.
let timesTwo x = x * 2
이것은 하나의 매개 변수 x
를 취하는 timesTwo
라는 함수를 정의합니다. 대화 형 F 번호 세션 (실행하면 fsharpi
OS X 및 Linux에 fsi.exe
Windows에서)을하고있는 해당 기능을 붙여 (그리고 추가 ;;
알려줍니다 fsharpi
당신이 방금 입력 한 코드를 평가)를, 당신은 그것을 볼 수 있습니다 다음으로 답하십시오.
val timesTwo : x:int -> int
즉, timesTwo
는 int
유형의 단일 매개 변수 x
를 사용하고 int
반환하는 함수입니다. 함수 시그니처는 매개 변수 이름없이 작성되는 경우가 많으므로이 함수 유형은 종종 int -> int
로 작성됩니다.
하지만 기다려! F #은 x
가 정수형 매개 변수라는 것을 어떻게 알았습니까? 그건 타입 유추 때문 입니다. 함수 본문에서 x
에 2
를 곱하기 때문에 x
와 2
의 유형은 동일해야합니다. 일반적으로 F #은 값을 다른 유형으로 내재적으로 변환하지 않으므로 원하는 유형 변환을 명시 적으로 지정해야합니다.
매개 변수를 사용하지 않는 함수를 만들려면 잘못된 방법입니다.
let hello = // This is a value, not a function
printfn "Hello world"
그것을 할 올바른 방법은 다음과 같습니다.
let hello () =
printfn "Hello world"
이 hello
함수는 "unit"유형으로 설명되는 unit -> unit
유형을가 집니다.
Curried 대 Tupled 함수
F #, Curried 함수 및 Tupled 함수에서 여러 매개 변수를 사용하여 함수를 정의하는 두 가지 방법이 있습니다.
let curriedAdd x y = x + y // Signature: x:int -> y:int -> int
let tupledAdd (x, y) = x + y // Signature: x:int * y:int -> int
F # 외부에서 정의 된 모든 함수 (예 : .NET 프레임 워크)는 F #에서 Tupled 형식으로 사용됩니다. F # 코어 모듈의 대부분의 기능은 Curried 형식과 함께 사용됩니다.
Curried 양식은 부분 적용을 허용하기 때문에 관용적 인 F #으로 간주됩니다. Tupled 형식에서는 다음 두 가지 예제 중 어느 것도 사용할 수 없습니다.
let addTen = curriedAdd 10 // Signature: int -> int
// Or with the Piping operator
3 |> curriedAdd 7 // Evaluates to 10
그 이유는 Curried 함수가 하나의 매개 변수로 호출되면 함수를 반환하기 때문입니다. 함수형 프로그래밍에 오신 것을 환영합니다 !!
let explicitCurriedAdd x = (fun y -> x + y) // Signature: x:int -> y:int -> int
let veryExplicitCurriedAdd = (fun x -> (fun y -> x + y)) // Same signature
정확히 동일한 서명임을 알 수 있습니다.
그러나 라이브러리를 작성할 때와 마찬가지로 다른 .NET 코드와 상호 작용할 때는 Tupled 형식을 사용하여 함수를 정의하는 것이 중요합니다.
인라인
인라이닝을 사용하면 함수 호출을 함수 본문으로 바꿀 수 있습니다.
이는 코드의 중요한 부분에서 성능상의 이유로 유용합니다. 그러나 상대방은 함수의 본문이 호출이 발생한 모든 곳에서 복제되기 때문에 어셈블리에 많은 공간이 필요하다는 것입니다. 함수를 인라인할지 어떨지를 결정할 때는주의해야합니다.
함수는 inline
키워드로 인라인 될 수 있습니다 :
// inline indicate that we want to replace each call to sayHello with the body
// of the function
let inline sayHello s1 =
sprintf "Hello %s" s1
// Call to sayHello will be replaced with the body of the function
let s = sayHello "Foo"
// After inlining ->
// let s = sprintf "Hello %s" "Foo"
printfn "%s" s
// Output
// "Hello Foo"
지역 값을 가진 또 다른 예 :
let inline addAndMulti num1 num2 =
let add = num1 + num2
add * 2
let i = addAndMulti 2 2
// After inlining ->
// let add = 2 + 2
// let i = add * 2
printfn "%i" i
// Output
// 8
파이프 전진 및 후진
파이프 연산자는 간단하고 우아한 방식으로 함수에 매개 변수를 전달하는 데 사용됩니다. 중간 값을 없애고 함수 호출을 읽기 쉽게 만듭니다.
F #에는 두 개의 파이프 연산자가 있습니다.
앞으로 (
|>
) : 매개 변수를 왼쪽에서 오른쪽으로 전달let print message = printf "%s" message // "Hello World" will be passed as a parameter to the print function "Hello World" |> print
뒤로 (
<|
) : 오른쪽에서 왼쪽으로 매개 변수 전달let print message = printf "%s" message // "Hello World" will be passed as a parameter to the print function print <| "Hello World"
다음은 파이프 연산자가없는 예입니다.
// We want to create a sequence from 0 to 10 then:
// 1 Keep only even numbers
// 2 Multiply them by 2
// 3 Print the number
let mySeq = seq { 0..10 }
let filteredSeq = Seq.filter (fun c -> (c % 2) = 0) mySeq
let mappedSeq = Seq.map ((*) 2) filteredSeq
let finalSeq = Seq.map (sprintf "The value is %i.") mappedSeq
printfn "%A" finalSeq
앞의 파이프 연산자를 사용하여 앞의 예제를 줄이고 깨끗하게 만들 수 있습니다.
// Using forward pipe, we can eliminate intermediate let binding
let finalSeq =
seq { 0..10 }
|> Seq.filter (fun c -> (c % 2) = 0)
|> Seq.map ((*) 2)
|> Seq.map (sprintf "The value is %i.")
printfn "%A" finalSeq
각 함수 결과는 매개 변수로 다음 함수로 전달됩니다.
파이프 매개 변수에 여러 매개 변수를 전달하려면 |
각 추가 매개 변수에 대해 매개 변수가있는 튜플을 만듭니다. 네이티브 F # 파이프 연산자는 최대 세 개의 매개 변수 (|||> 또는 <|||)를 지원합니다.
let printPerson name age =
printf "My name is %s, I'm %i years old" name age
("Foo", 20) ||> printPerson