Jika fungsi saya memenuhi di bawah dua persyaratan, saya percaya berfungsi Sum
untuk mengembalikan penjumlahan item dalam daftar di mana item mengevaluasi true untuk kondisi yang diberikan memenuhi syarat untuk disebut sebagai fungsi murni, bukan?
1) Untuk set i / p yang diberikan, o / p yang sama dikembalikan terlepas dari waktu ketika fungsi dipanggil
2) Tidak memiliki efek samping
public int Sum(Func<int,bool> predicate, IEnumerable<int> numbers){
int result = 0;
foreach(var item in numbers)
if(predicate(item)) result += item;
return result;
}
Contoh: Sum(x=>x%2==0, new List<int> {1,2,3,4,5...100});
Alasan saya mengajukan pertanyaan ini adalah karena saya melihat hampir di mana-mana orang menyarankan untuk menghindari operator penugasan dan loop karena itu adalah gaya pemrograman yang sangat penting. Jadi apa yang salah dengan contoh di atas yang menggunakan loop dan operator penugasan dalam konteks pemrograman fungsi?
sumber
item
variabel dimutasi dalam loop.Jawaban:
Apa itu dalam pemrograman fungsional yang membuat perbedaan?
Pemrograman fungsional pada prinsipnya bersifat deklaratif . Anda mengatakan apa hasil Anda alih-alih bagaimana cara menghitungnya.
Mari kita lihat implementasi dari snippet Anda yang sangat fungsional. Dalam Haskell akan menjadi:
Apakah jelas apa hasilnya? Cukup begitu, itu adalah jumlah angka yang memenuhi predikat. Bagaimana cara menghitungnya? Saya tidak peduli, tanyakan pada kompiler.
Anda bisa mengatakan bahwa menggunakan
sum
danfilter
merupakan trik dan itu tidak masuk hitungan. Biarkan menerapkannya tanpa bantuan ini (meskipun cara terbaik adalah menerapkannya terlebih dahulu).Solusi "Pemrograman Fungsional 101" yang tidak digunakan
sum
adalah dengan rekursi:Masih cukup jelas apa hasilnya dalam hal panggilan fungsi tunggal. Itu bisa
0
, ataurecursive call + h or 0
, tergantung padapred h
. Masih cukup lurus ke depan, bahkan jika hasil akhirnya tidak segera jelas (meskipun dengan sedikit latihan ini benar-benar berbunyi sepertifor
lingkaran).Bandingkan dengan versi Anda:
Apa hasilnya? Oh, saya melihat: lajang
return
pernyataan, tidak ada kejutan di sini:return result
.Tapi apa itu
result
?int result = 0
? Sepertinya tidak benar. Anda melakukan sesuatu nanti dengan itu0
. Oke, Anda menambahkannyaitem
. Dan seterusnya.Tentu saja, bagi sebagian besar programmer ini cukup jelas apa yang terjadi dalam fungsi sederhana seperti ini, tetapi tambahkan beberapa
return
pernyataan tambahan dan tiba-tiba semakin sulit untuk dilacak. Semua kode adalah tentang bagaimana , dan apa yang tersisa bagi pembaca untuk mencari tahu - ini jelas gaya yang sangat penting .Jadi, apakah variabel dan loop salah?
Tidak.
Ada banyak hal yang lebih mudah dijelaskan oleh mereka, dan banyak algoritma yang membutuhkan keadaan bisa berubah menjadi cepat. Tetapi variabel pada dasarnya imperatif, menjelaskan bagaimana alih-alih apa , dan memberikan sedikit prediksi tentang nilai mereka mungkin beberapa baris kemudian atau setelah beberapa iterasi loop. Loop biasanya membutuhkan keadaan yang masuk akal, dan karenanya mereka pada dasarnya juga penting.
Variabel dan loop sama sekali bukan pemrograman fungsional.
Ringkasan
Pemrograman fungsi kontemporsial sedikit lebih gaya dan cara berpikir yang berguna daripada paradigma. Preferensi kuat untuk fungsi murni ada dalam pola pikir ini, tetapi sebenarnya hanya sebagian kecil saja.
Sebagian besar bahasa yang tersebar luas memungkinkan Anda menggunakan beberapa konstruksi fungsional. Misalnya dalam Python Anda dapat memilih antara:
atau
atau
Ekspresi fungsional ini cocok untuk masalah semacam itu dan hanya membuat kode lebih pendek (dan lebih pendek bagus ). Anda seharusnya tidak mengganti kode imperatif dengan mereka, tetapi ketika mereka cocok, mereka hampir selalu merupakan pilihan yang lebih baik.
sumber
Penggunaan keadaan yang bisa berubah umumnya tidak disarankan dalam pemrograman fungsional. Loop dikecilkan sebagai konsekuensinya, karena loop hanya berguna dalam kombinasi dengan keadaan bisa berubah.
Fungsi secara keseluruhan adalah murni, yang hebat, tetapi paradigma pemrograman fungsional tidak hanya berlaku di tingkat keseluruhan fungsi. Anda juga ingin menghindari keadaan tidak bisa berubah juga di tingkat lokal, fungsi di dalam. Dan alasannya pada dasarnya sama: Menghindari keadaan yang bisa berubah membuat kode lebih mudah dipahami dan mencegah bug tertentu.
Dalam kasus Anda, Anda bisa menulis
numbers.Where(predicate).Sum()
yang jelas lebih sederhana. Dan lebih sederhana berarti lebih sedikit bug.sumber
Where
dinumbers.Where(predicate).Sum()
- itu menggunakanforeach
loop.Meskipun Anda benar bahwa dari sudut pandang pengamat eksternal,
Sum
fungsi Anda murni, implementasi internal jelas tidak murni - Anda memiliki status tersimpan diresult
mana Anda berulang kali bermutasi. Salah satu alasan untuk menghindari keadaan tidak bisa berubah adalah karena menghasilkan beban kognitif yang lebih besar pada programmer, yang pada gilirannya menyebabkan lebih banyak bug [rujukan?] .Sementara dalam contoh sederhana seperti ini, jumlah keadaan yang dapat diubah yang disimpan mungkin cukup kecil sehingga tidak akan menyebabkan masalah serius, prinsip umum masih berlaku. Contoh mainan seperti
Sum
mungkin bukan cara terbaik untuk menggambarkan keunggulan pemrograman fungsional daripada keharusan - coba lakukan sesuatu dengan banyak keadaan yang bisa berubah dan keuntungannya bisa menjadi lebih jelas.sumber