Parameter dan Argumen
Topik ini mendeskripsikan dukungan bahasa untuk menentukan parameter serta meneruskan argumen ke fungsi, metode, dan properti. Ini termasuk informasi tentang cara melewati referensi, serta cara menentukan dan menggunakan metode yang bisa mengambil jumlah variabel dari argumen.
Parameter dan Argumen
Istilah parameter digunakan untuk menjelaskan nama untuk nilai yang diharapkan untuk disediakan. Istilah argumen digunakan untuk nilai yang disediakan untuk setiap parameter.
Parameter bisa ditentukan dalam bentuk tuple atau curried, atau dalam beberapa kombinasi keduanya. Anda bisa meneruskan argumen dengan menggunakan nama parameter eksplisit. Parameter dari metode dapat ditentukan sebagai opsional dan diberikan nilai default.
Pola Parameter
Parameter yang disediakan ke fungsi dan metode, secara umum, pola dipisahkan oleh spasi. Ini berarti bahwa, pada prinsipnya, pola apa pun yang dijelaskan dalam Ekspresi Kecocokan dapat digunakan dalam daftar parameter untuk fungsi atau anggota.
Metode biasanya menggunakan bentuk tuple dari argumen passing. Ini mencapai hasil yang lebih jelas dari perspektif bahasa .NET lainnya karena bentuk tuple cocok dengan cara argumen diteruskan dalam metode .NET.
Bentuk curried paling sering digunakan dengan fungsi yang dibuat dengan menggunakan pengikatan let
.
Pseudocode berikut menunjukkan contoh argumen tuple serta curried.
// Tuple form.
member this.SomeMethod(param1, param2) = ...
// Curried form.
let function1 param1 param2 = ...
Bentuk gabungan dimungkinkan ketika beberapa argumen berada dalam tuple dan beberapa tidak.
let function2 param1 (param2a, param2b) param3 = ...
Pola lain juga bisa digunakan dalam daftar parameter, tetapi jika pola parameter tidak cocok dengan semua input yang mungkin, mungkin ada kecocokan yang tidak lengkap pada durasi. Pengecualian MatchFailureException
dihasilkan ketika nilai argumen tidak cocok dengan pola yang ditentukan dalam daftar parameter. Kompilator mengeluarkan peringatan saat pola parameter memungkinkan kecocokan yang tidak lengkap. Setidaknya satu pola lain biasanya berguna untuk daftar parameter, dan itu adalah pola wildcard. Anda menggunakan pola wildcard dalam daftar parameter ketika Anda hanya ingin mengabaikan argumen apa pun yang disediakan. Kode berikut mengilustrasikan penggunaan pola wildcard dalam daftar argumen.
let makeList _ = [ for i in 1 .. 100 -> i * i ]
// The arguments 100 and 200 are ignored.
let list1 = makeList 100
let list2 = makeList 200
Pola wildcard dapat berguna setiap kali Anda tidak memerlukan argumen yang diteruskan, seperti di titik masuk utama ke program, ketika Anda tidak tertarik dengan argumen baris perintah yang biasanya disediakan sebagai string array, seperti dalam kode berikut.
[<EntryPoint>]
let main _ =
printfn "Entry point!"
0
Pola lain yang terkadang digunakan dalam argumen adalah pola as
, dan pola pengidentifikasi yang terkait dengan serikat yang didiskriminasi dan pola aktif. Anda bisa menggunakan pola gabungan yang didiskriminasi kasus tunggal sebagai berikut.
type Slice = Slice of int * int * string
let GetSubstring1 (Slice(p0, p1, text)) =
printfn "Data begins at %d and ends at %d in string %s" p0 p1 text
text[p0..p1]
let substring = GetSubstring1 (Slice(0, 4, "Et tu, Brute?"))
printfn "Substring: %s" substring
Outputnya sebagai berikut.
Data begins at 0 and ends at 4 in string Et tu, Brute?
Et tu
Pola aktif bisa berguna sebagai parameter, misalnya, saat mengubah argumen menjadi format yang diinginkan, seperti dalam contoh berikut:
type Point = { x : float; y : float }
let (| Polar |) { x = x; y = y} =
( sqrt (x*x + y*y), System.Math.Atan (y/ x) )
let radius (Polar(r, _)) = r
let angle (Polar(_, theta)) = theta
Anda bisa menggunakan pola as
untuk menyimpan nilai yang cocok sebagai nilai lokal, seperti yang ditunjukkan dalam baris kode berikut.
let GetSubstring2 (Slice(p0, p1, text) as s) = s
Pola lain yang sesekali digunakan adalah fungsi yang membiarkan argumen terakhir tidak disebutkan namanya dengan penyediaan, sebagai isi fungsi, ekspresi lambda yang segera melakukan kecocokan pola pada argumen implisit. Contohnya adalah baris kode berikut ini.
let isNil = function [] -> true | _::_ -> false
Kode ini mendefinisikan fungsi yang mengambil daftar generik serta mengembalikan true
jika daftar kosong, dan false
jika sebaliknya. Penggunaan teknik tersebut bisa membuat kode lebih sulit dibaca.
Terkadang, pola yang melibatkan kecocokan yang tidak lengkap berguna, misalnya, jika Anda tahu bahwa daftar dalam program Anda hanya mempunyai tiga elemen, Anda mungkin menggunakan pola seperti berikut ini dalam daftar parameter.
let sum [a; b; c;] = a + b + c
Penggunaan pola yang mempunyai kecocokan yang tidak lengkap paling baik disediakan untuk pembuatan prototipe cepat dan penggunaan sementara lainnya. Pengompilasi akan mengeluarkan peringatan untuk kode tersebut. Pola tersebut tidak bisa mencakup kasus umum dari semua input yang mungkin dan oleh karena itu tidak cocok untuk API komponen.
Argumen Bernama
Argumen untuk metode bisa ditentukan oleh posisi dalam daftar argumen yang dipisahkan koma, atau dapat diteruskan ke metode secara eksplisit dengan memberikan nama, diikuti dengan tanda sama dengan dan nilai yang akan diteruskan. Jika ditentukan dengan memberikan nama, mereka dapat muncul dalam urutan yang berbeda dari yang digunakan dalam deklarasi.
Argumen bernama dapat membuat kode lebih mudah dibaca dan lebih mudah beradaptasi dengan jenis perubahan tertentu dalam API, seperti menyusun ulang parameter metode.
Argumen bernama hanya diperbolehkan untuk metode, bukan untuk fungsi let
terikat, nilai fungsi, atau ekspresi lambda.
Contoh kode berikut menunjukkan penggunaan dari argumen bernama.
type SpeedingTicket() =
member this.GetMPHOver(speed: int, limit: int) = speed - limit
let CalculateFine (ticket : SpeedingTicket) =
let delta = ticket.GetMPHOver(limit = 55, speed = 70)
if delta < 20 then 50.0 else 100.0
let ticket1 : SpeedingTicket = SpeedingTicket()
printfn "%f" (CalculateFine ticket1)
Dalam panggilan ke konstruktor kelas, Anda bisa mengatur nilai properti kelas dengan menggunakan sintaks yang mirip dengan argumen bernama. Contoh berikut menunjukkan sintaks ini.
type Account() =
let mutable balance = 0.0
let mutable number = 0
let mutable firstName = ""
let mutable lastName = ""
member this.AccountNumber
with get() = number
and set(value) = number <- value
member this.FirstName
with get() = firstName
and set(value) = firstName <- value
member this.LastName
with get() = lastName
and set(value) = lastName <- value
member this.Balance
with get() = balance
and set(value) = balance <- value
member this.Deposit(amount: float) = this.Balance <- this.Balance + amount
member this.Withdraw(amount: float) = this.Balance <- this.Balance - amount
let account1 = new Account(AccountNumber=8782108,
FirstName="Darren", LastName="Parker",
Balance=1543.33)
Untuk informasi selengkapnya, lihat Konstruktor (F#).
Teknik yang sama, dimaksudkan untuk memanggil setter properti, juga berlaku untuk metode pengembalian objek apa pun (seperti metode pabrik):
type Widget() =
member val Width = 1 with get,set
member val Height = 1 with get,set
type WidgetFactory =
static member MakeNewWidget() =
new Widget()
static member AdjustWidget(w: Widget) =
w
let w = WidgetFactory.MakeNewWidget(Width=10)
w.Width // = 10
w.Height // = 1
WidgetFactory.AdjustWidget(w, Height=10)
w.Height // = 10
Perhatikan bahwa anggota tersebut dapat melakukan pekerjaan arbitrer apa pun, sintaksnya secara efektif merupakan tangan singkat untuk memanggil setter properti sebelum mengembalikan nilai akhir.
Parameter Opsional
Anda dapat menentukan parameter opsional untuk metode dengan menggunakan tanda tanya di depan nama parameter. Dari perspektif penerima panggilan, parameter opsional ditafsirkan sebagai jenis opsi F#, sehingga Anda dapat mengkuerinya dengan cara reguler jenis opsi dikueri, dengan menggunakan match
ekspresi dengan Some
dan None
. Parameter opsional hanya diizinkan pada anggota, bukan pada fungsi yang dibuat dengan menggunakan pengikatan let
.
Anda bisa meneruskan nilai opsional yang ada ke metode menurut nama parameter, seperti ?arg=None
atau ?arg=Some(3)
, atau ?arg=arg
. Ini dapat berguna saat membangun metode yang meneruskan argumen opsional ke metode yang lain.
Anda juga dapat menggunakan fungsi defaultArg
, yang menetapkan nilai default dari argumen opsional. Fungsi defaultArg
mengambil parameter opsional sebagai argumen pertama dan nilai default sebagai yang kedua.
Contoh berikut mengilustrasikan penggunaan dari parameter opsional.
type DuplexType =
| Full
| Half
type Connection(?rate0 : int, ?duplex0 : DuplexType, ?parity0 : bool) =
let duplex = defaultArg duplex0 Full
let parity = defaultArg parity0 false
let mutable rate = match rate0 with
| Some rate1 -> rate1
| None -> match duplex with
| Full -> 9600
| Half -> 4800
do printfn "Baud Rate: %d Duplex: %A Parity: %b" rate duplex parity
let conn1 = Connection(duplex0 = Full)
let conn2 = Connection(duplex0 = Half)
let conn3 = Connection(300, Half, true)
let conn4 = Connection(?duplex0 = None)
let conn5 = Connection(?duplex0 = Some(Full))
let optionalDuplexValue : option<DuplexType> = Some(Half)
let conn6 = Connection(?duplex0 = optionalDuplexValue)
Outputnya sebagai berikut.
Baud Rate: 9600 Duplex: Full Parity: false
Baud Rate: 4800 Duplex: Half Parity: false
Baud Rate: 300 Duplex: Half Parity: true
Baud Rate: 9600 Duplex: Full Parity: false
Baud Rate: 9600 Duplex: Full Parity: false
Baud Rate: 4800 Duplex: Half Parity: false
Untuk tujuan dari C# dan Visual Basic, Anda dapat menggunakan atribut [<Optional; DefaultParameterValue<(...)>]
di F#, sehingga pemanggil akan melihat argumen sebagai opsional. Ini setara dengan menentukan argumen sebagai opsional dalam C# seperti pada MyMethod(int i = 3)
.
open System
open System.Runtime.InteropServices
type C =
static member Foo([<Optional; DefaultParameterValue("Hello world")>] message) =
printfn $"{message}"
Anda juga bisa menentukan objek baru sebagai nilai parameter default. Misalnya, anggota Foo
dapat memiliki CancellationToken
opsional sebagai input:
open System.Threading
open System.Runtime.InteropServices
type C =
static member Foo([<Optional; DefaultParameterValue(CancellationToken())>] ct: CancellationToken) =
printfn $"{ct}"
Nilai yang diberikan sebagai argumen ke DefaultParameterValue
harus cocok dengan jenis parameternya. Misalnya, hal berikut tidak diperbolehkan:
type C =
static member Wrong([<Optional; DefaultParameterValue("string")>] i:int) = ()
Dalam hal ini, pengompilasi menghasilkan peringatan dan akan mengabaikan kedua atribut seluruhnya. Perhatikan bahwa nilai default null
harus diberi anotasi jenis, karena jika tidak, pengompilasi menyimpulkan jenis yang salah, yaitu [<Optional; DefaultParameterValue(null:obj)>] o:obj
.
Meneruskan Berdasarkan Referensi
Meneruskan nilai F# menurut referensi melibatkan byrefs, yang merupakan jenis pointer terkelola. Panduan mengenai jenis mana yang akan digunakan adalah sebagai berikut:
- Gunakan
inref<'T>
jika Anda hanya perlu membaca penunjuk. - Gunakan
outref<'T>
jika Anda hanya perlu menulis ke penunjuk. - Gunakan
byref<'T>
jika Anda perlu membaca dari serta menulis ke penunjuk.
let example1 (x: inref<int>) = printfn $"It's %d{x}"
let example2 (x: outref<int>) = x <- x + 1
let example3 (x: byref<int>) =
printfn $"It's %d{x}"
x <- x + 1
let test () =
// No need to make it mutable, since it's read-only
let x = 1
example1 &x
// Needs to be mutable, since we write to it
let mutable y = 2
example2 &y
example3 &y // Now 'y' is 3
Karena parameter adalah penunjuk dan nilainya dapat diubah, setiap perubahan pada nilai dipertahankan setelah eksekusi dari fungsi.
Anda dapat menggunakan tuple sebagai nilai kembali untuk menyimpan parameter out
apa pun dalam metode pustaka .NET. Atau, Anda bisa memperlakukan parameter out
sebagai parameter byref
. Contoh kode berikut menggambarkan kedua cara.
// TryParse has a second parameter that is an out parameter
// of type System.DateTime.
let (b, dt) = System.DateTime.TryParse("12-20-04 12:21:00")
printfn "%b %A" b dt
// The same call, using an address of operator.
let mutable dt2 = System.DateTime.Now
let b2 = System.DateTime.TryParse("12-20-04 12:21:00", &dt2)
printfn "%b %A" b2 dt2
Larik Parameter
Kadang-kadang perlu untuk mendefinisikan fungsi yang mengambil sejumlah parameter jenis heterogen. Tidak akan praktis untuk membuat semua metode kelebihan beban yang mungkin untuk memperhitungkan semua jenis yang bisa digunakan. Implementasi .NET memberikan dukungan untuk metode tersebut melalui fitur array parameter. Metode yang mengambil array parameter dalam tanda tangannya dapat disediakan dengan jumlah parameter yang dapat berubah. Parameter dimasukkan ke dalam array. Jenis elemen array menentukan jenis parameter yang bisa diteruskan ke fungsi. Jika Anda menentukan array parameter dengan System.Object
sebagai jenis elemen, maka kode klien bisa meneruskan nilai dari jenis apa pun.
Di F#, array parameter hanya bisa ditentukan dalam metode. Mereka tidak dapat digunakan dalam fungsi mandiri atau fungsi yang didefinisikan dalam modul.
Anda menentukan array parameter dengan menggunakan atribut ParamArray
. Atribut ParamArray
hanya bisa diterapkan ke parameter terakhir.
Kode berikut mengilustrasikan pemanggilan metode .NET yang mengambil array parameter dan definisi jenis di F# yang memiliki metode yang mengambil array parameter.
open System
type X() =
member this.F([<ParamArray>] args: Object[]) =
for arg in args do
printfn "%A" arg
[<EntryPoint>]
let main _ =
// call a .NET method that takes a parameter array, passing values of various types
Console.WriteLine("a {0} {1} {2} {3} {4}", 1, 10.0, "Hello world", 1u, true)
let xobj = new X()
// call an F# method that takes a parameter array, passing values of various types
xobj.F("a", 1, 10.0, "Hello world", 1u, true)
0
Saat dijalankan dalam proyek, output kode sebelumnya adalah sebagai berikut:
a 1 10 Hello world 1 True
"a"
1
10.0
"Hello world"
1u
true