Saya percaya saya telah mempelajari beberapa / banyak / sebagian besar konsep dasar yang mendasari pemrograman fungsional dalam JavaScript. Namun, saya memiliki masalah khusus membaca kode fungsional, bahkan kode yang saya tulis, dan bertanya-tanya apakah ada yang bisa memberi saya petunjuk, tips, praktik terbaik, terminologi, dll. Yang dapat membantu.
Ambil kode di bawah ini. Saya menulis kode ini. Ini bertujuan untuk menetapkan kesamaan persen antara dua objek, antara katakan {a:1, b:2, c:3, d:3}
dan {a:1, b:1, e:2, f:2, g:3, h:5}
. Saya menghasilkan kode sebagai jawaban atas pertanyaan ini pada Stack Overflow . Karena saya tidak yakin persis berapa persen kesamaan yang ditanyakan oleh poster itu, saya memberikan empat jenis:
- persen kunci dalam objek 1 yang dapat ditemukan di 2,
- persentase nilai dalam objek 1 yang dapat ditemukan di ke-2, termasuk duplikat,
- persentase nilai dalam objek 1 yang dapat ditemukan di ke-2, tanpa duplikat diizinkan, dan
- persentase {key: value} berpasangan di objek 1 yang dapat ditemukan di objek ke-2.
Saya mulai dengan kode yang cukup penting, tetapi segera menyadari bahwa ini adalah masalah yang cocok untuk pemrograman fungsional. Secara khusus, saya menyadari bahwa jika saya dapat mengekstrak satu atau tiga fungsi untuk masing-masing dari empat strategi di atas yang menentukan jenis fitur yang ingin saya bandingkan (misalnya kunci, atau nilai, dll.), Maka saya mungkin menjadi mampu mengurangi (mengampuni permainan kata-kata) sisa kode menjadi unit berulang. Anda tahu, menjaganya tetap KERING. Jadi saya beralih ke pemrograman fungsional. Saya cukup bangga dengan hasilnya, saya pikir itu cukup elegan, dan saya pikir saya mengerti apa yang saya lakukan dengan cukup baik.
Namun, bahkan setelah menulis kode sendiri dan memahami setiap bagian dari itu selama konstruksi, ketika saya sekarang melihat kembali, saya terus menjadi lebih bingung tentang cara membaca setengah garis tertentu, serta cara "grok" apa yang dilakukan oleh setengah baris kode tertentu. Saya menemukan diri saya membuat panah mental untuk menghubungkan bagian-bagian yang berbeda yang dengan cepat terdegradasi menjadi berantakan spageti.
Jadi, adakah yang bisa memberi tahu saya cara "membaca" beberapa bit kode yang lebih berbelit-belit dengan cara yang ringkas dan berkontribusi pada pemahaman saya tentang apa yang saya baca? Saya kira bagian yang paling membuat saya adalah mereka yang memiliki beberapa panah gemuk berturut-turut dan / atau bagian yang memiliki beberapa tanda kurung berturut-turut. Sekali lagi, pada intinya, pada akhirnya saya bisa mengetahui logikanya, tetapi (saya harap) ada cara yang lebih baik untuk dilakukan dengan cepat dan jelas dan langsung "mengambil" serangkaian pemrograman JavaScript fungsional.
Jangan ragu menggunakan baris kode apa pun dari bawah, atau bahkan contoh lainnya. Namun, jika Anda ingin beberapa saran awal dari saya, berikut adalah beberapa. Mulailah dengan yang cukup sederhana. Dari dekat akhir kode, ada ini yang dilewatkan sebagai parameter ke fungsi: obj => key => obj[key]
. Bagaimana seseorang membaca dan memahaminya? Sebuah contoh lagi adalah salah satu fungsi penuh dari dekat awal: const getXs = (obj, getX) => Object.keys(obj).map(key => getX(obj)(key));
. Bagian terakhir map
membuat saya khususnya.
Harap dicatat, pada saat ini dalam waktu saya tidak mencari referensi untuk Haskell atau notasi abstrak simbolis atau dasar-dasar currying, dll Apa yang saya sedang cari adalah kalimat bahasa Inggris yang saya diam-diam bisa mulut sambil melihat baris kode. Jika Anda memiliki referensi yang secara khusus membahas hal itu, bagus, tetapi saya juga tidak mencari jawaban yang mengatakan saya harus membaca beberapa buku teks dasar. Saya telah melakukan itu dan saya mendapatkan (setidaknya sejumlah besar) logika. Perhatikan juga, saya tidak perlu jawaban lengkap (meskipun upaya seperti itu akan diterima): Bahkan jawaban singkat yang memberikan cara yang elegan untuk membaca satu baris tertentu dari kode yang merepotkan akan dihargai.
Saya kira bagian dari pertanyaan ini adalah: Dapatkah saya membaca kode fungsional secara linear, Anda tahu, dari kiri ke kanan dan dari atas ke bawah? Atau apakah seseorang cukup dipaksa untuk membuat gambaran mental kabel spaghetti seperti pada halaman kode yang jelas tidak linier? Dan jika seseorang harus melakukan itu, kita masih harus membaca kodenya, jadi bagaimana kita mengambil teks linear dan memasang spageti?
Setiap tips akan sangat dihargai.
const obj1 = { a:1, b:2, c:3, d:3 };
const obj2 = { a:1, b:1, e:2, f:2, g:3, h:5 };
// x or X is key or value or key/value pair
const getXs = (obj, getX) =>
Object.keys(obj).map(key => getX(obj)(key));
const getPctSameXs = (getX, filter = vals => vals) =>
(objA, objB) =>
filter(getXs(objB, getX))
.reduce(
(numSame, x) =>
getXs(objA, getX).indexOf(x) > -1 ? numSame + 1 : numSame,
0
) / Object.keys(objA).length * 100;
const pctSameKeys = getPctSameXs(obj => key => key);
const pctSameValsDups = getPctSameXs(obj => key => obj[key]);
const pctSameValsNoDups = getPctSameXs(obj => key => obj[key], vals => [...new Set(vals)]);
const pctSameProps = getPctSameXs(obj => key => JSON.stringify( {[key]: obj[key]} ));
console.log('obj1:', JSON.stringify(obj1));
console.log('obj2:', JSON.stringify(obj2));
console.log('% same keys: ', pctSameKeys (obj1, obj2));
console.log('% same values, incl duplicates:', pctSameValsDups (obj1, obj2));
console.log('% same values, no duplicates: ', pctSameValsNoDups(obj1, obj2));
console.log('% same properties (k/v pairs): ', pctSameProps (obj1, obj2));
// output:
// obj1: {"a":1,"b":2,"c":3,"d":3}
// obj2: {"a":1,"b":1,"e":2,"f":2,"g":3,"h":5}
// % same keys: 50
// % same values, incl duplicates: 125
// % same values, no duplicates: 75
// % same properties (k/v pairs): 25
sumber
Saya belum melakukan banyak pekerjaan yang sangat fungsional dalam Javascript (yang akan saya katakan ini - kebanyakan orang berbicara tentang Javascript fungsional mungkin menggunakan peta, filter dan pengurangan, tetapi kode Anda mendefinisikan sendiri fungsi tingkat yang lebih tinggi , yang merupakan agak lebih maju dari itu), tetapi saya telah melakukannya di Haskell, dan saya pikir setidaknya beberapa pengalaman diterjemahkan. Saya akan memberi Anda beberapa petunjuk untuk hal-hal yang telah saya pelajari:
Menentukan jenis fungsi sangat penting. Haskell tidak mengharuskan Anda untuk menentukan jenis fungsi, tetapi memasukkan jenis dalam definisi membuatnya lebih mudah dibaca. Meskipun Javascript tidak mendukung pengetikan eksplisit dengan cara yang sama, tidak ada alasan untuk tidak memasukkan definisi tipe dalam komentar, misalnya:
Dengan sedikit latihan dalam bekerja dengan definisi tipe seperti ini, mereka membuat makna dari suatu fungsi menjadi lebih jelas.
Penamaan itu penting, bahkan mungkin lebih daripada dalam pemrograman prosedural. Banyak program fungsional ditulis dalam gaya yang sangat singkat yang berat pada konvensi (misalnya konvensi bahwa 'xs' adalah daftar / larik dan 'x' adalah item di dalamnya sangat meresap), tetapi kecuali Anda mengerti gaya itu mudah saya sarankan lebih banyak penamaan verbose. Melihat nama-nama spesifik yang Anda gunakan, "getX" agak tidak jelas, dan karenanya "getXs" tidak terlalu banyak membantu. Saya akan memanggil "getXs" sesuatu seperti "applyToProperties", dan "getX" mungkin akan menjadi "propertyMapper". "getPctSameXs" kemudian akan menjadi "persenPropertiesSameWith" ("dengan").
Hal penting lainnya adalah menulis kode idiomatik . Saya perhatikan bahwa Anda menggunakan sintaks
a => b => some-expression-involving-a-and-b
untuk menghasilkan fungsi kari. Ini menarik, dan bisa berguna dalam beberapa situasi, tetapi Anda tidak melakukan apa pun di sini yang mendapat manfaat dari fungsi kari dan itu akan menjadi Javascript yang lebih idiomatis untuk menggunakan fungsi multi-argumen tradisional sebagai gantinya. Melakukan hal itu dapat membuatnya lebih mudah untuk melihat apa yang sedang terjadi dalam sekejap. Anda juga menggunakanconst name = lambda-expression
untuk mendefinisikan fungsi, di mana akan lebih idiomatis untuk digunakanfunction name (args) { ... }
. Saya tahu mereka sedikit berbeda secara semantik, tetapi kecuali jika Anda mengandalkan perbedaan itu, saya sarankan menggunakan varian yang lebih umum bila memungkinkan.sumber
obj => key => ...
dapat disederhanakan(obj, key) => ...
karena nantinyagetX(obj)(key)
juga dapat disederhanakanget(obj, key)
. Sebaliknya, fungsi kari lain(getX, filter = vals => vals) => (objA, objB) => ...
,, tidak dapat dengan mudah disederhanakan, setidaknya dalam konteks kode lainnya seperti yang ditulis.