函式Functions

函式是所有程式設計語言的基礎程式執行單位。Functions are the fundamental unit of program execution in any programming language. 如同其他語言,F# 函式有名稱、可以有參數並且接受引數,而且也有主體。As in other languages, an F# function has a name, can have parameters and take arguments, and has a body. F# 也支援函式程式設計建構,例如將函式視為值、在運算式中使用不具名函式、組合函式以形成新函式、局部調用函式,以及透過部分套用函式引數的隱含定義函式。F# also supports functional programming constructs such as treating functions as values, using unnamed functions in expressions, composition of functions to form new functions, curried functions, and the implicit definition of functions by way of the partial application of function arguments.

您可以使用 let 關鍵字定義函式,若是遞迴函式,則可以使用 let rec 關鍵字組合來定義函式。You define functions by using the let keyword, or, if the function is recursive, the let rec keyword combination.

語法Syntax

// 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

備註Remarks

function-name 表示函式的識別項。The function-name is an identifier that represents the function. parameter-list 由以空格分隔的連續參數所組成。The parameter-list consists of successive parameters that are separated by spaces. 您可以指定每個參數的明確類型,如<參數>一節中所述。You can specify an explicit type for each parameter, as described in the Parameters section. 若未指定特定的引數類型,編譯器會嘗試從函式主體推斷類型。If you do not specify a specific argument type, the compiler attempts to infer the type from the function body. function-body 由運算式組成。The function-body consists of an expression. 函式主體通常是由組合運算式組成,其中包含數個會產生最終運算式作為傳回值的運算式。The expression that makes up the function body is typically a compound expression consisting of a number of expressions that culminate in a final expression that is the return value. return-type 是選擇性項目,包含後面接著類型的冒號。The return-type is a colon followed by a type and is optional. 若您沒有明確指定傳回值的類型,則編譯器會從最終運算式判斷傳回型別。If you do not specify the type of the return value explicitly, the compiler determines the return type from the final expression.

簡單的函式定義如下所示:A simple function definition resembles the following:

let f x = x + 1

在上述範例中,函式名稱為 f,引數是類型為 intx,函式主體為 x + 1,而傳回值的類型為 intIn the previous example, the function name is f, the argument is x, which has type int, the function body is x + 1, and the return value is of type int.

函式可以標記為 inlineFunctions can be marked inline. 如需 inline 的資訊,請參閱內嵌函式For information about inline, see Inline Functions.

範圍Scope

在模組範圍以外的任何範圍層級,重複使用值或函式名稱並非錯誤。At any level of scope other than module scope, it is not an error to reuse a value or function name. 若重複使用名稱,後面宣告的名稱會遮蔽前面宣告的名稱。If you reuse a name, the name declared later shadows the name declared earlier. 不過,在模組中的最上層範圍,名稱必須是唯一名稱。However, at the top level scope in a module, names must be unique. 例如,下列程式碼出現在模組範圍時會產生錯誤,但在函式內時則不會:For example, the following code produces an error when it appears at module scope, but not when it appears inside a function:

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

但是,下列程式碼在任何範圍層級都可以使用:But the following code is acceptable at any level of scope:

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

參數Parameters

參數名稱必須列於函式名稱後面。Names of parameters are listed after the function name. 您可以指定參數的類型,如下列範例所示:You can specify a type for a parameter, as shown in the following example:

let f (x : int) = x + 1

若您指定類型,請將它放在參數名稱之後,並且以冒號來分隔類型與名稱。If you specify a type, it follows the name of the parameter and is separated from the name by a colon. 若您省略此參數的類型,編譯器便會推斷參數類型。If you omit the type for the parameter, the parameter type is inferred by the compiler. 例如,在下列函式定義中,引數 x 經推斷為 int 類型,因為 1 是 int 類型 。For example, in the following function definition, the argument x is inferred to be of type int because 1 is of type int.

let f x = x + 1

不過,編譯器會嘗試將函式盡可能推斷成為泛型。However, the compiler will attempt to make the function as generic as possible. 例如,請注意下列程式碼:For example, note the following code:

let f x = (x, x)

函式會從任何類型的一個引數建立元組。The function creates a tuple from one argument of any type. 因為沒有指定類型,所以函式可以與任何引數類型搭配使用。Because the type is not specified, the function can be used with any argument type. 如需詳細資訊,請參閱自動產生For more information, see Automatic Generalization.

函式主體Function Bodies

函式主體可以包含區域變數和函式的定義。A function body can contain definitions of local variables and functions. 這類變數和函式是在目前函式主體的範圍內,而不是在主體以外。Such variables and functions are in scope in the body of the current function but not outside it. 啟用輕量型語法選項時,必須使用縮排來表示定義是在函式主體中,如下列範例所示:When you have the lightweight syntax option enabled, you must use indentation to indicate that a definition is in a function body, as shown in the following example:

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

如需詳細資訊,請參閱程式碼格式化方針詳細語法For more information, see Code Formatting Guidelines and Verbose Syntax.

傳回值Return Values

編譯器會使用函式主體中的最終運算式,來判斷傳回值和類型。The compiler uses the final expression in a function body to determine the return value and type. 編譯器可能會從先前的運算式推斷最終運算式的類型。The compiler might infer the type of the final expression from previous expressions. 在上節示範的 cylinderVolume 函式中,pi 的類型是從常值 3.14159 的類型經判斷為 floatIn the function cylinderVolume, shown in the previous section, the type of pi is determined from the type of the literal 3.14159 to be float. 編譯器會使用 pi 的類型,判斷運算式 h * pi * r * r 的類型為 floatThe compiler uses the type of pi to determine the type of the expression h * pi * r * r to be float. 因此,函式的整體傳回型別為 floatTherefore, the overall return type of the function is float.

若要明確指定傳回值,請撰寫如下的程式碼:To specify the return value explicitly, write the code as follows:

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

撰寫如上所示的程式碼時,編譯器會將 float 套用至整個函式。若您也要將它套用至參數類型,請使用下列程式碼:As the code is written above, the compiler applies float to the entire function; if you mean to apply it to the parameter types as well, use the following code:

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

呼叫函式Calling a Function

您可以指定函式名稱,後面接著空格,再接著以空格分隔的任何引數來呼叫函式。You call functions by specifying the function name followed by a space and then any arguments separated by spaces. 例如,若要呼叫函式 cylinderVolume 並將結果指派給值 vol,您可以撰寫下列程式碼:For example, to call the function cylinderVolume and assign the result to the value vol, you write the following code:

let vol = cylinderVolume 2.0 3.0

部分套用引數Partial Application of Arguments

若您提供的引數數目比指定的數目更少,則必須建立需要其餘引數的新函式。If you supply fewer than the specified number of arguments, you create a new function that expects the remaining arguments. 這個處理引數的方法稱為「局部調用」 ,是 F# 這類函式程式設計語言的特色。This method of handling arguments is referred to as currying and is a characteristic of functional programming languages like F#. 例如,假設您要使用兩種大小的管子,其中一個半徑為 2.0,另一個半徑為 3.0For example, suppose you are working with two sizes of pipe: one has a radius of 2.0 and the other has a radius of 3.0. 您可以建立會判斷管子容量的函式,如下所示:You could create functions that determine the volume of pipe as follows:

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

接著,視需要為兩個不同大小的各種管子長度提供其他引數:You would then supply the additional argument as needed for various lengths of pipe of the two different sizes:

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

遞迴函式Recursive Functions

「遞迴函式」 是會自我呼叫的函式。Recursive functions are functions that call themselves. 您必須在 let 關鍵字後面指定 rec 關鍵字來使用遞迴函式。They require that you specify the rec keyword following the let keyword. 請從函式主體中叫用遞迴函式,就像叫用任何函式呼叫一樣。Invoke the recursive function from within the body of the function just as you would invoke any function call. 下列遞迴函式會計算 n 個的斐波上數位。The following recursive function computes the nth Fibonacci number. Fibonacci 數字序列自古聞名,此序列中的每個連續數字都是前兩個數字的總和。The Fibonacci number sequence has been known since antiquity and is a sequence in which each successive number is the sum of the previous two numbers in the sequence.

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

若不謹慎撰寫遞迴函式,而且也不了解特殊技巧 (例如累計值和接續符號的用法),某些遞迴函式便可能會使程式堆疊溢位,或導致執行時沒有效率。Some recursive functions might overflow the program stack or perform inefficiently if you do not write them with care and with awareness of special techniques, such as the use of accumulators and continuations.

函式值Function Values

在 F# 中,所有函式都視為值,而實際上它們稱為「函式值」 。In F#, all functions are considered values; in fact, they are known as function values. 由於函式都是值,因此可以作為其他函式的引數,或在其他會用到這些值的內容中使用。Because functions are values, they can be used as arguments to other functions or in other contexts where values are used. 以下是接受函式值作為引數的函式範例:Following is an example of a function that takes a function value as an argument:

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

您可以使用 -> 語彙基元來指定函式值的類型。You specify the type of a function value by using the -> token. 在此語彙基元左邊是引數的類型,右邊則是傳回值。On the left side of this token is the type of the argument, and on the right side is the return value. 在上述範例中,apply1 函式會接受 transform 函式作為引數,其中 transform 是接受整數並傳回另一個整數的函式。In the previous example, apply1 is a function that takes a function transform as an argument, where transform is a function that takes an integer and returns another integer. 在下列範例程式碼中,會示範 apply1 的用法:The following code shows how to use apply1:

let increment x = x + 1

let result1 = apply1 increment 100

在上述程式碼執行之後,result 的值會是 101。The value of result will be 101 after the previous code runs.

多個引數是以連續 -> 語彙基元分隔,如下列範例所示:Multiple arguments are separated by successive -> tokens, as shown in the following example:

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

let mul x y = x * y

let result2 = apply2 mul 10 20

結果為 200。The result is 200.

Lambda 運算式Lambda Expressions

「Lambda 運算式」 是不具名函式。A lambda expression is an unnamed function. 在上述範例中,您可以使用 Lambda 運算式,而不定義具名函式 incrementmul,如下所示:In the previous examples, instead of defining named functions increment and mul, you could use lambda expressions as follows:

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

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

您可以透過 fun 關鍵字定義 Lambda 運算式。You define lambda expressions by using the fun keyword. Lambda 運算式類似函式定義,但是它使用 -> 語彙基元來分隔引數清單與函式主體,而不是 = 語彙基元。A lambda expression resembles a function definition, except that instead of the = token, the -> token is used to separate the argument list from the function body. 如同一般函式定義,您可以推斷或明確指定引數類型,而 Lambda 運算式的傳回型別是從主體中最後一個運算式的類型來推斷。As in a regular function definition, the argument types can be inferred or specified explicitly, and the return type of the lambda expression is inferred from the type of the last expression in the body. 如需詳細資訊, 請參閱 Lambda 運算式:fun關鍵字。For more information, see Lambda Expressions: The fun Keyword.

函式組合和管線Function Composition and Pipelining

F# 中的函式可以從其他函式組合。Functions in F# can be composed from other functions. 兩個函式 function1function2 會組合成另一個函式,表示先套用 function1,接著套用 function2The composition of two functions function1 and function2 is another function that represents the application of function1 followed the application of function2:

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

結果為 202。The result is 202.

管線可讓函式呼叫鏈結在一起成為連續作業。Pipelining enables function calls to be chained together as successive operations. 管線運作方式如下:Pipelining works as follows:

let result = 100 |> function1 |> function2

結果同樣是 202。The result is again 202.

組合運算子會採用兩個函式,並傳回一個函式。相較之下,管線運算子則採用一個函式與一個引數,並傳回一個值。The composition operators take two functions and return a function; by contrast, the pipeline operators take a function and an argument and return a value. 下列程式碼範例可透過示範函式簽章和使用方式的不同,顯示管線和組合運算子之間的差異。The following code example shows the difference between the pipeline and composition operators by showing the differences in the function signatures and usage.

// 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
// ( |> ) : 'T1 -> ('T1 -> 'U) -> 'U
let Pipeline2 x = addOne x |> timesTwo

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

// Result is 5
let result3 = Pipeline1 2

// Result is 6
let result4 = Pipeline2 2

多載函式Overloading Functions

您可以多載類型的方法,但不是函式的方法。You can overload methods of a type but not functions. 如需詳細資訊,請參閱方法For more information, see Methods.

另請參閱See also