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. Parameter opsional ditafsirkan sebagai jenis opsi F#, sehingga Anda bisa mengkuerinya dengan cara seperti jenis opsi dikueri biasanya, dengan menggunakan ekspresi match 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

Lihat juga