Rahasia masakan JavaScript: rempah-rempah

Lihatlah potongan kode berikut yang memecahkan masalah yang sama, dan pikirkan yang mana yang paling Anda sukai.
Ini yang pertama:Ini yang kedua:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] .filter(int => isEven(int)) .filter(int => isBiggerThan(3, int)) .map(int => int + 1) .map(int => toChar(int)) .filter(char => !isVowel(char)) .join('') // 'fhjl' 
 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] .filter(isEven) .filter(isBiggerThan(3)) .map(plus(1)) .map(toChar) .filter(not(isVowel)) .join('') // 'fhjl' 
”Saya bertaruh bahwa opsi kedua adalah keterbacaan yang jauh lebih baik daripada yang pertama,” kata penulis materi, terjemahan yang kami terbitkan hari ini. Menurutnya, seluruh poinnya ada pada argumen metode filter() dan map() .



Hari ini kita akan berbicara tentang cara mendaur ulang kode yang mirip dengan contoh pertama sehingga terlihat seperti kode dari yang kedua. Penulis artikel berjanji bahwa setelah Anda memahami cara kerjanya, Anda akan berhubungan dengan program Anda dengan cara yang baru dan Anda tidak akan dapat mengabaikan apa yang sebelumnya tampak normal dan tidak memerlukan perbaikan.

Fungsi sederhana


Pertimbangkan fungsi sum() sederhana yang menambahkan angka yang diteruskan ke dalamnya:

 const sum = (a, b) => a + b sum(1, 2) // 3 

Kami menulis ulang, memberikan fungsi baru nama csum() :

 const csum = a => b => a + b csum(1)(2) // 3 

Versi barunya bekerja dengan cara yang persis sama dengan versi aslinya, satu-satunya perbedaan adalah bagaimana fungsi baru ini disebut. Yaitu, fungsi sum() mengambil dua parameter sekaligus, dan csum() mengambil parameter yang sama satu per satu. Bahkan, saat memanggil csum() , dua fungsi dipanggil. Secara khusus, pertimbangkan situasi ketika csum() dipanggil, csum() nomor 1 dan tidak ada yang lain:

 csum(1) // b => 1 + b 

Panggilan ke csum() mengarah pada fakta bahwa ia mengembalikan fungsi yang dapat menerima argumen numerik kedua yang diteruskan ke csum() selama panggilan biasa, dan mengembalikan hasil menambahkan satu ke argumen ini. Panggil fungsi ini plusOne() :

 const plusOne = csum(1) plusOne(2) // 3 

Bekerja dengan array


Dalam JavaScript, Anda dapat bekerja dengan array menggunakan berbagai metode khusus. Katakanlah metode map() digunakan untuk menerapkan fungsi yang diteruskan ke setiap elemen array.

Misalnya, untuk menambah 1 elemen setiap array integer (lebih tepatnya, untuk membentuk array baru yang mengandung elemen array asli meningkat sebesar 1), Anda dapat menggunakan konstruksi berikut:

 [1, 2, 3].map(x => x + 1) // [2, 3, 4] 

Dengan kata lain, apa yang terjadi dapat digambarkan sebagai berikut: fungsi x => x + 1 mengambil bilangan bulat dan mengembalikan angka yang mengikutinya dalam serangkaian bilangan bulat. Menggunakan fungsi plusOne() dibahas di atas, contoh ini dapat ditulis ulang sebagai berikut:

 [1, 2, 3].map(x => plusOne(x)) // [2, 3, 4] 

Di sini perlu untuk memperlambat dan berpikir tentang apa yang terjadi. Jika Anda melakukan ini, Anda dapat melihat bahwa dalam kasus yang dipertimbangkan, konstruksi x => plusOne(x) dan plusOne (perhatikan bahwa dalam situasi ini tidak ada tanda kurung setelah nama fungsi) adalah setara. Untuk lebih memahami hal ini, pertimbangkan fungsi otherPlusOne() :

 const otherPlusOne = x => plusOne(x) otherPlusOne(1) // 2 

Hasil dari fungsi ini akan sama dengan yang diperoleh dengan panggilan sederhana ke plusOne() sudah kita kenal:

 plusOne(1) // 2 

Untuk alasan yang sama, kita dapat berbicara tentang kesetaraan dua konstruksi berikut. Inilah yang pertama yang telah kita lihat:

 [1, 2, 3].map(x => plusOne(x)) // [2, 3, 4] 

Ini yang kedua:

 [1, 2, 3].map(plusOne) // [2, 3, 4] 

Selain itu, ingatlah bagaimana fungsi plusOne() dibuat:

 const plusOne = csum(1) 

Ini memungkinkan kami untuk menulis ulang konstruksi kami dengan map() sebagai berikut:

 [1, 2, 3].map(csum(1)) // [2, 3, 4] 

Sekarang, dengan menggunakan teknik yang sama, isBiggerThan() . Jika Anda mau, coba lakukan sendiri, lalu baca terus. Ini akan menghilangkan penggunaan konstruksi yang tidak perlu saat menggunakan metode filter() . Pertama, mari bawa kode ke formulir ini:

 const isBiggerThan = (threshold, int) => int > threshold [1, 2, 3, 4].filter(int => isBiggerThan(3, int)) 

Kemudian, menyingkirkan semua yang tidak perlu, kami mendapatkan kode yang sudah Anda lihat di awal materi ini:

 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] .filter(isEven) .filter(isBiggerThan(3)) .map(plus(1)) .map(toChar) .filter(not(isVowel)) .join('') // 'fhjl' 

Kami sekarang mempertimbangkan dua aturan sederhana yang memungkinkan Anda untuk menulis kode dengan gaya yang dibahas di sini.

Peraturan nomor 1


Dua konstruksi berikut ini setara:

 […].map(x => fnc(x)) […].map(fnc) 

Peraturan nomor 2


Callback selalu dapat ditulis ulang untuk mengurangi jumlah argumen yang digunakan untuk menyebutnya:

 const fnc = (x, y, z) => … […].map(x => fnc(x, y, z)) const fnc = (y, z) => x => … […].map(fnc(y, z)) 

Jika Anda sendiri yang menulis fungsi isBiggerThan() , maka Anda mungkin sudah beralih ke transformasi seperti itu. Misalkan kita membutuhkan angka yang lebih besar dari 3 untuk melewati filter. Ini dapat dilakukan seperti ini:

 const isBiggerThan = (threshold, int) => int > threshold […].filter(int => isBiggerThan(3, int)) 

Sekarang kita menulis ulang fungsi isBiggerThan() sehingga dapat digunakan dalam metode filter() dan tidak menggunakan int=> construct:

 const isBiggerThan = threshold => int => int > threshold […].map(isBiggerThan(3)) 

Latihan


Misalkan kita memiliki fragmen kode berikut:

 const keepGreatestChar = (char1, char2) => char1 > char2 ? char1 : char2 keepGreatestChar('b', 'f') // 'f' //   'f'   'b' 

Sekarang, berdasarkan fungsi keepGreatestChar() , buat fungsi keepGreatestCharBetweenBAnd() . Kita memerlukan itu, dengan memanggilnya, kita hanya bisa menyampaikan satu argumen, sementara itu akan membandingkan karakter yang diteruskan dengan karakter b . Fungsi ini mungkin terlihat seperti ini:

 const keepGreatestChar = (char1, char2) => char1 > char2 ? char1 : char2 const keepGreatestCharBetweenBAnd = char => keepGreatestChar('b', char) keepGreatestCharBetweenBAnd('a') // 'b' //   'b'   'a' 

Sekarang tuliskan fungsi greatestCharInArray() , yang, menggunakan fungsi keepGreatestChar() dalam metode array keepGreatestChar() , memungkinkan Anda untuk mencari karakter "terbesar" dan tidak memerlukan argumen. Mari kita mulai dengan kode ini:

 const keepGreatestChar = (char1, char2) => char1 > char2 ? char1 : char2 const greatestCharInArray = array => array.reduce((acc, char) => acc > char ? acc : char, 'a') greatestCharInArray(['a', 'b', 'c', 'd']) // 'd' 

Untuk mengatasi masalah ini, terapkan fungsi creduce() , yang dapat digunakan dalam fungsi greatestCharInArray() , yang memungkinkan, dalam aplikasi praktis fungsi ini, untuk tidak meneruskan apa pun padanya kecuali array untuk menemukan karakter dengan kode terbesar.

Fungsi creduce() harus cukup universal sehingga dapat digunakan untuk menyelesaikan masalah yang membutuhkan penggunaan kemampuan dari metode array creduce() standar. Dengan kata lain, fungsi harus menerima panggilan balik, nilai awal, dan array untuk bekerja dengannya. Akibatnya, Anda harus mendapatkan fungsi yang dapat digunakan fragmen kode berikut:

 const greatestCharInArray = creduce(keepGreatestChar, 'a') greatestCharInArray(['a', 'b', 'c', 'd']) // 'd' 

Ringkasan


Mungkin sekarang Anda memiliki pertanyaan tentang mengapa metode, diproses sesuai dengan metodologi yang disajikan di sini, memiliki nama yang dimulai dengan karakter c . Karakter c adalah akronim untuk kari, dan kami berbicara tentang bagaimana fungsi kari membantu meningkatkan keterbacaan kode. Perlu dicatat bahwa di sini kami tidak berusaha keras untuk mematuhi prinsip-prinsip pemrograman fungsional, tetapi, kami percaya bahwa aplikasi praktis dari apa yang dibahas di sini memungkinkan kami untuk meningkatkan kode. Jika topik currying dalam JavaScript menarik untuk Anda - disarankan untuk membaca bab 4 buku ini tentang pemrograman fungsional, dan, secara umum, karena Anda telah mencapai titik ini - baca seluruh buku ini. Juga, jika Anda baru mengenal pemrograman fungsional, lihat materi pemula ini .

Pembaca yang budiman! Apakah Anda menggunakan currying fungsi dalam pengembangan JavaScript?

Source: https://habr.com/ru/post/id415059/


All Articles