Dalam posting sebelumnya tentang currying, kami melihat bagaimana fungsi dengan beberapa parameter dibagi menjadi fungsi yang lebih kecil, dengan satu parameter. Ini adalah solusi yang benar secara matematis, tetapi ada alasan lain untuk melakukannya - ini juga mengarah pada teknik yang sangat kuat yang disebut aplikasi fungsi parsial . Gaya ini sangat banyak digunakan dalam pemrograman fungsional, dan sangat penting untuk memahaminya.

Penggunaan sebagian fungsi
Gagasan aplikasi parsial adalah bahwa jika kita memperbaiki parameter N pertama dari fungsi, kita mendapatkan fungsi baru dengan parameter yang tersisa. Dari pembahasan currying, orang bisa melihat bagaimana aplikasi parsial terjadi secara alami.
Beberapa contoh sederhana untuk menggambarkan:
// "" + 42 let add42 = (+) 42 // add42 1 add42 3 // // [1;2;3] |> List.map add42 // "" let twoIsLessThan = (<) 2 // twoIsLessThan 1 twoIsLessThan 3 // twoIsLessThan [1;2;3] |> List.filter twoIsLessThan // "" printfn let printer = printfn "printing param=%i" // printer [1;2;3] |> List.iter printer
Dalam setiap kasus, kami membuat fungsi yang diterapkan sebagian yang dapat digunakan kembali dalam situasi yang berbeda.
Dan tentu saja, aplikasi parsial membuatnya mudah untuk memperbaiki parameter fungsi. Berikut ini beberapa contohnya:
// List.map let add1 = (+) 1 let add1ToEach = List.map add1 // "add1" List.map // add1ToEach [1;2;3;4] // List.filter let filterEvens = List.filter (fun i -> i%2 = 0) // // filterEvens [1;2;3;4]
Contoh berikut yang lebih kompleks mengilustrasikan bagaimana pendekatan yang sama dapat digunakan untuk secara transparan menciptakan perilaku "tertanam".
- Kami membuat fungsi yang menambahkan dua angka, tetapi selain itu dibutuhkan fungsi logging yang akan mencatat angka-angka ini dan hasilnya.
- Fungsi logging mengambil dua parameter: (string) "name" dan (generic) "value", oleh karena itu ia memiliki
string->'a->unit
signature string->'a->unit
. - Kemudian kami membuat berbagai implementasi dari fungsi logging, seperti logger konsol atau logger berbasis pop-up.
- Dan akhirnya, kami menerapkan sebagian fungsi utama untuk membuat fungsi baru, dengan logger tertutup.
// - let adderWithPluggableLogger logger xy = logger "x" x logger "y" y let result = x + y logger "x+y" result result // - let consoleLogger argName argValue = printfn "%s=%A" argName argValue // let addWithConsoleLogger = adderWithPluggableLogger consoleLogger addWithConsoleLogger 1 2 addWithConsoleLogger 42 99 // - let popupLogger argName argValue = let message = sprintf "%s=%A" argName argValue System.Windows.Forms.MessageBox.Show( text=message,caption="Logger") |> ignore // - let addWithPopupLogger = adderWithPluggableLogger popupLogger addWithPopupLogger 1 2 addWithPopupLogger 42 99
Fungsi logger tertutup ini dapat digunakan seperti fungsi lainnya. Sebagai contoh, kita dapat membuat aplikasi parsial untuk menambahkan 42, dan kemudian meneruskannya ke fungsi daftar, seperti yang kita lakukan untuk fungsi add42
sederhana.
// 42 let add42WithConsoleLogger = addWithConsoleLogger 42 [1;2;3] |> List.map add42WithConsoleLogger [1;2;3] |> List.map add42 //
Fungsi yang diterapkan sebagian adalah alat yang sangat berguna. Kami dapat membuat fungsi perpustakaan yang fleksibel (meskipun kompleks), dan mudah untuk membuatnya dapat digunakan kembali secara default, sehingga kompleksitas akan disembunyikan dari kode klien.
Desain Fungsi Parsial
Jelas, urutan parameter dapat secara serius mempengaruhi kenyamanan penggunaan parsial. Sebagai contoh, sebagian besar fungsi dalam List
seperti List.map
dan List.filter
memiliki bentuk yang sama, yaitu:
List-function [function parameter(s)] [list]
Daftar ini selalu menjadi parameter terakhir. Beberapa contoh dalam bentuk lengkap:
List.map (fun i -> i+1) [0;1;2;3] List.filter (fun i -> i>1) [0;1;2;3] List.sortBy (fun i -> -i ) [0;1;2;3]
Contoh yang sama menggunakan aplikasi parsial:
let eachAdd1 = List.map (fun i -> i+1) eachAdd1 [0;1;2;3] let excludeOneOrLess = List.filter (fun i -> i>1) excludeOneOrLess [0;1;2;3] let sortDesc = List.sortBy (fun i -> -i) sortDesc [0;1;2;3]
Jika fungsi perpustakaan diimplementasikan dengan urutan argumen yang berbeda, aplikasi parsial akan jauh lebih tidak nyaman.
Ketika Anda menulis fungsi Anda dengan banyak parameter, Anda dapat memikirkan urutan terbaiknya. Seperti dengan semua masalah desain, tidak ada jawaban "benar", tetapi ada beberapa rekomendasi yang diterima secara umum.
- Mulai dengan opsi yang cenderung statis.
- Jadilah yang terakhir untuk mengatur struktur atau koleksi data (atau parameter perubahan lainnya)
- Untuk pemahaman yang lebih baik tentang operasi seperti pengurangan, disarankan untuk mengamati urutan yang diharapkan
Tip pertama sederhana. Parameter yang cenderung "diperbaiki" oleh aplikasi parsial harus pergi dulu, seperti dalam contoh dengan logger di atas.
Mengikuti tip kedua membuatnya lebih mudah untuk menggunakan operator dan komposisi perpipaan. Kami telah melihat ini berkali-kali dalam contoh dengan fungsi di atas daftar.
// let result = [1..10] |> List.map (fun i -> i+1) |> List.filter (fun i -> i>5)
Demikian pula, sebagian fungsi yang diterapkan pada daftar mudah terpapar pada komposisi parameter daftar dapat dihilangkan:
let compositeOp = List.map (fun i -> i+1) >> List.filter (fun i -> i>5) let result = compositeOp [1..10]
Pembungkus fungsi BCL parsial
Fungsi .NET base class library (BCL) dari .NET mudah diakses dari F #, tetapi mereka dirancang tanpa bergantung pada bahasa fungsional seperti F #. Sebagai contoh, sebagian besar fungsi memerlukan parameter data di awal, sedangkan di F # parameter data umumnya menjadi yang terakhir.
Namun, Anda dapat dengan mudah menulis pembungkus untuk membuat fungsi-fungsi ini lebih idiomatis. Dalam contoh di bawah ini, fungsi .NET string ditulis ulang sehingga string target digunakan lebih dulu daripada yang pertama:
// .NET let replace oldStr newStr (s:string) = s.Replace(oldValue=oldStr, newValue=newStr) let startsWith lookFor (s:string) = s.StartsWith(lookFor)
Setelah string menjadi parameter terakhir, Anda dapat menggunakan fungsi-fungsi ini dalam jalur pipa, seperti biasa:
let result = "hello" |> replace "h" "j" |> startsWith "j" ["the"; "quick"; "brown"; "fox"] |> List.filter (startsWith "f")
atau dalam komposisi fungsi:
let compositeOp = replace "h" "j" >> startsWith "j" let result = compositeOp "hello"
Memahami operator conveyor
Setelah Anda melihat sebagian aplikasi dalam bisnis, Anda dapat memahami cara kerja fungsi pipelined.
Fungsi perpipaan didefinisikan sebagai berikut:
let (|>) xf = fx
Yang dia lakukan adalah menempatkan argumen di depan fungsi, bukan setelahnya.
let doSomething xyz = x+y+z doSomething 1 2 3 //
Dalam kasus ketika fungsi f
memiliki beberapa parameter, dan parameter terakhir dari fungsi f
akan bertindak sebagai nilai input x
pipa. Bahkan, fungsi yang ditransfer f
telah diterapkan sebagian dan hanya mengharapkan satu parameter - nilai input untuk pipelining (te x
).
Berikut adalah contoh serupa yang ditulis ulang untuk penggunaan sebagian.
let doSomething xy = let intermediateFn z = x+y+z intermediateFn // intermediateFn let doSomethingPartial = doSomething 1 2 doSomethingPartial 3 // 3 |> doSomethingPartial // ,
Seperti yang telah Anda lihat, operator perpipaan sangat umum di F #, dan digunakan kapan pun Anda ingin mempertahankan aliran alami data. Beberapa contoh lain yang mungkin Anda temui:
"12" |> int // "12" int 1 |> (+) 2 |> (*) 3 //
Operator Konveyor Balik
Dari waktu ke waktu, Anda dapat menemukan operator pipa terbalik "<|".
let (<|) fx = fx
Fungsi ini sepertinya tidak melakukan apa-apa, jadi mengapa itu ada?
Alasannya adalah bahwa ketika operator pipa terbalik digunakan sebagai operator biner gaya infiks, itu mengurangi kebutuhan untuk tanda kurung, yang membuat kode lebih bersih.
printf "%i" 1+2 // printf "%i" (1+2) // printf "%i" <| 1+2 //
Anda dapat menggunakan pipa dalam dua arah sekaligus untuk mendapatkan notasi pseudo-infix.
let add xy = x + y (1+2) add (3+4) // 1+2 |> add <| 3+4 //
Sumber Daya Tambahan
Ada banyak tutorial untuk F #, termasuk materi untuk mereka yang datang dengan pengalaman C # atau Java. Tautan berikut mungkin berguna saat Anda masuk lebih dalam ke F #:
Beberapa cara lain untuk mulai belajar F # juga dijelaskan.
Akhirnya, komunitas F # sangat ramah pemula. Ada obrolan yang sangat aktif di Slack, didukung oleh F # Software Foundation, dengan kamar pemula yang dapat Anda gabung dengan bebas . Kami sangat menyarankan Anda melakukan ini!
Jangan lupa untuk mengunjungi situs komunitas berbahasa Rusia F # ! Jika Anda memiliki pertanyaan tentang belajar bahasa, dengan senang hati kami akan membahasnya di ruang obrolan:
Tentang penulis terjemahan
Diterjemahkan oleh @kleidemos
Perubahan terjemahan dan editorial dilakukan oleh upaya komunitas pengembang F # berbahasa Rusia . Kami juga berterima kasih kepada @schvepsss dan @shwars karena telah menyiapkan artikel ini untuk dipublikasikan.