수색…


둘 이상의 매개 변수의 함수

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

즉, timesTwoint 유형의 단일 매개 변수 x 를 사용하고 int 반환하는 함수입니다. 함수 시그니처는 매개 변수 이름없이 작성되는 경우가 많으므로이 함수 유형은 종종 int -> int 로 작성됩니다.

하지만 기다려! F #은 x 가 정수형 매개 변수라는 것을 어떻게 알았습니까? 그건 타입 유추 때문 입니다. 함수 본문에서 x2 를 곱하기 때문에 x2 의 유형은 동일해야합니다. 일반적으로 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


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow