Apa perbedaan antara bahasa pemrograman fungsional dan imperatif?


159

Sebagian besar bahasa arus utama, termasuk bahasa pemrograman berorientasi objek (OOP) seperti C #, Visual Basic, C ++, dan Java dirancang untuk terutama mendukung pemrograman imperatif (prosedural), sedangkan bahasa Haskell / gofer seperti murni berfungsi. Adakah yang bisa menguraikan apa perbedaan antara kedua cara pemrograman ini?

Saya tahu itu tergantung pada kebutuhan pengguna untuk memilih cara pemrograman tetapi mengapa dianjurkan untuk belajar bahasa pemrograman fungsional?



1
periksa ini [posting] [1] lainnya. Ini jelas menggambarkan perbedaan. [1]: stackoverflow.com/questions/602444/…
theta

Jawaban:


160

Definisi: Bahasa imperatif menggunakan urutan pernyataan untuk menentukan cara mencapai tujuan tertentu. Pernyataan-pernyataan ini dikatakan mengubah keadaan program karena masing-masing dieksekusi pada gilirannya.

Contoh: Java adalah bahasa imperatif. Misalnya, sebuah program dapat dibuat untuk menambahkan serangkaian angka:

 int total = 0;
 int number1 = 5;
 int number2 = 10;
 int number3 = 15;
 total = number1 + number2 + number3; 

Setiap pernyataan mengubah status program, dari menetapkan nilai ke setiap variabel hingga penambahan akhir dari nilai-nilai tersebut. Dengan menggunakan urutan lima pernyataan, program secara eksplisit diberitahu cara menambahkan angka 5, 10 dan 15 bersama-sama.

Bahasa fungsional: Paradigma pemrograman fungsional secara eksplisit dibuat untuk mendukung pendekatan fungsional murni untuk pemecahan masalah. Pemrograman fungsional adalah bentuk pemrograman deklaratif.

Keuntungan Fungsi Murni: Alasan utama untuk menerapkan transformasi fungsional sebagai fungsi murni adalah bahwa fungsi murni dapat dikomposisikan: yaitu mandiri dan tanpa kewarganegaraan. Karakteristik ini membawa sejumlah manfaat, termasuk yang berikut: Peningkatan keterbacaan dan pemeliharaan. Ini karena setiap fungsi dirancang untuk menyelesaikan tugas tertentu mengingat argumennya. Fungsi ini tidak bergantung pada kondisi eksternal apa pun.

Perkembangan pengulangan yang lebih mudah. Karena kode lebih mudah direvisi, perubahan desain sering kali lebih mudah diterapkan. Misalnya, Anda menulis transformasi yang rumit, dan kemudian menyadari bahwa beberapa kode diulang beberapa kali dalam transformasi. Jika Anda refactor melalui metode murni, Anda dapat memanggil metode murni sesuka hati tanpa khawatir tentang efek samping.

Pengujian dan debugging yang lebih mudah. Karena fungsi murni dapat lebih mudah diuji secara terpisah, Anda dapat menulis kode uji yang memanggil fungsi murni dengan nilai tipikal, case edge yang valid, dan case edge yang tidak valid.

Untuk Orang OOP atau bahasa Imperatif:

Bahasa berorientasi objek baik ketika Anda memiliki serangkaian operasi pada hal-hal dan ketika kode Anda berkembang, Anda terutama menambahkan hal-hal baru. Ini dapat dicapai dengan menambahkan kelas baru yang menerapkan metode yang ada dan kelas yang ada dibiarkan sendiri.

Bahasa fungsional baik ketika Anda memiliki satu set hal yang tetap dan ketika kode Anda berkembang, Anda terutama menambahkan operasi baru pada hal-hal yang ada. Ini dapat dicapai dengan menambahkan fungsi baru yang menghitung dengan tipe data yang ada dan fungsi yang ada dibiarkan sendiri.

Cons:

Itu tergantung pada persyaratan pengguna untuk memilih cara pemrograman, sehingga ada salahnya hanya ketika pengguna tidak memilih cara yang tepat.

Ketika evolusi salah jalan, Anda memiliki masalah:

  • Menambahkan operasi baru ke program berorientasi objek mungkin memerlukan pengeditan banyak definisi kelas untuk menambahkan metode baru
  • Menambahkan hal baru ke program fungsional mungkin memerlukan pengeditan banyak definisi fungsi untuk menambahkan kasus baru.

10
Fungsi murni dalam hal ini setara dengan fungsi matematika. Input yang sama akan selalu dipetakan ke output yang sama. Mereka juga tidak memiliki efek samping (selain mengembalikan nilai atau nilai) yang berarti kompiler dapat melakukan beberapa optimasi keren, dan membuatnya lebih mudah untuk menjalankan fungsi secara paralel karena tidak ada yang perlu diperdebatkan.
WorBlux

Jadi, cara yang benar dan praktik terbaik untuk menyusun aplikasi oop yang dapat dipelihara dan diuji cenderung untuk merancang kode imperatif dengan kondisi pikiran declerative?
Kemal Gültekin

4
Saya tidak melihat perbedaan yang jelas pada teks di mana fitur setiap pemrograman disorot. Sebagian besar deskripsi untuk pemrograman prosedural dapat dipertukarkan dengan teks pemrograman imperatif dan sebaliknya.
AxeEffect

7
Jawaban ini mencoba untuk menjelaskan apa itu pemrograman fungsional tetapi bahkan tidak repot-repot mendefinisikan apa fungsi murni itu. Saya tidak melihat bagaimana orang bisa membaca jawaban ini dan merasa percaya diri mengetahui perbedaan antara pemrograman deklaratif dan prosedural.
Ringo

230

Inilah perbedaannya:

Imperatif:

  • Mulailah
  • Nyalakan sepatu Anda ukuran 9 1/2.
  • Beri ruang di saku Anda untuk menyimpan berbagai [7] kunci.
  • Taruh kunci di ruangan untuk kunci di saku.
  • Masuk garasi.
  • Buka garasi.
  • Masukkan Mobil.

... dan seterusnya dan seterusnya ...

  • Taruh susu di lemari es.
  • Berhenti.

Deklaratif, sedangkan fungsional adalah subkategori:

  • Susu adalah minuman sehat, kecuali jika Anda kesulitan mencerna laktosa.
  • Biasanya, satu menyimpan susu di lemari es.
  • Kulkas adalah sebuah kotak yang membuat barang-barang di dalamnya tetap dingin.
  • Toko adalah tempat barang dijual.
  • Yang dimaksud dengan "menjual" adalah pertukaran barang dengan uang.
  • Juga, pertukaran uang untuk sesuatu disebut "membeli".

... dan seterusnya dan seterusnya ...

  • Pastikan kami memiliki susu di lemari es (saat kami membutuhkannya - untuk bahasa fungsional yang malas).

Rangkuman: Dalam bahasa imperatif Anda memberi tahu komputer cara mengubah bit, byte, dan kata-kata dalam memori itu dan dalam urutan apa. Dalam fungsional, kami memberi tahu komputer apa hal, tindakan, dll. Sebagai contoh, kita katakan bahwa faktorial 0 adalah 1, dan faktorial dari setiap bilangan alami lainnya adalah produk dari angka tersebut dan faktorial dari pendahulunya. Kita tidak mengatakan: Untuk menghitung faktorial dari n, cadangan wilayah memori dan simpan 1 di sana, lalu gandakan angka di wilayah memori itu dengan angka 2 ke n dan simpan hasilnya di tempat yang sama, dan pada akhirnya, wilayah memori akan berisi faktorial.


1
Terima kasih. Itu cara yang bagus untuk melihatnya.
L-Samuels

5
Saya menyukai penjelasan Anda @ Igno, tetapi ada sesuatu yang tidak jelas bagi saya. Dalam Deklaratif, meskipun Anda hanya memberi tahu hal-hal , tetapi tetap saja Anda perlu mengubah bit dan membuat perubahan pada status di mesin untuk melanjutkan dengan benar. Itu membuat saya bingung, bahwa entah bagaimana Deklaratif mirip dengan Pemrograman Prosedural (seperti Fungsi C) , dan masih ada perbedaan besar di antara mereka secara internal. Bukankah Fungsi C sama dengan Fungsi dalam pemrograman Fungsional (pada Level Mesin)?
phoenisx

11
@Igno, Seperti Subroto, saya tidak begitu mengerti penjelasan Anda. Sepertinya apa yang Anda tulis dapat diringkas sebagai: Butuh jawaban ... dapatkan jawaban. tampaknya mengabaikan bit-bit penting yang bagaimana. Saya tidak mengerti bagaimana Anda bisa menyembunyikan bagian itu dari pengguna, pada titik tertentu seseorang harus tahu bagaimana hal itu dilakukan ... Anda tidak bisa menyimpan penyihir di balik tirai selamanya.
Brett Thomas

3
Ini tidak jauh dari apa yang saya pahami pemrograman fungsional menjadi. Saya pikir pemrograman fungsional adalah penghapusan input dan output tersembunyi dari fungsi.
Ringo

7
Penjelasan berbelit-belit.
JoeTidee

14

Sebagian besar bahasa modern memiliki tingkat imperatif dan fungsional yang bervariasi tetapi untuk lebih memahami pemrograman fungsional, akan lebih baik untuk mengambil contoh bahasa fungsional murni seperti Haskell dalam kontras kode imperatif dalam bahasa yang tidak begitu fungsional seperti java / c #. Saya percaya itu selalu mudah untuk dijelaskan dengan contoh, jadi di bawah ini adalah satu.

Pemrograman fungsional: menghitung faktorial n yaitu n! yaitu nx (n-1) x (n-2) x ... x 2 X 1

-- | Haskell comment goes like
-- | below 2 lines is code to calculate factorial and 3rd is it's execution  

factorial 0 = 1
factorial n = n * factorial (n - 1)
factorial 3

-- | for brevity let's call factorial as f; And x => y shows order execution left to right
-- | above executes as := f(3) as 3 x f(2) => f(2) as 2 x f(1) => f(1) as 1 x f(0) => f(0) as 1  
-- | 3 x (2 x (1 x (1)) = 6

Perhatikan bahwa Haskel memungkinkan overloading fungsi ke tingkat nilai argumen. Sekarang di bawah ini adalah contoh kode imperatif dalam meningkatkan tingkat imperativeness:

//somewhat functional way
function factorial(n) {
  if(n < 1) {
     return 1;
  }
  return n * factorial(n-1);   
}
factorial(3);

//somewhat more imperative way
function imperativeFactor(n) {
  int f = 1
  for(int i = 1; i <= n; i++) {
     f = f * i
  }
  return f;
}

Bacaan ini bisa menjadi referensi yang baik untuk memahami bahwa bagaimana kode imperatif lebih fokus pada bagaimana bagian, keadaan mesin (i in for loop), urutan eksekusi, flow control.

Contoh selanjutnya dapat dilihat sebagai kode java / c # lang secara kasar dan bagian pertama sebagai batasan bahasa itu sendiri berbeda dengan Haskell untuk membebani fungsi dengan nilai (nol) dan karenanya dapat dikatakan itu bukan bahasa fungsional murni, di sisi lain tangan Anda dapat mengatakan itu mendukung prog fungsional. sampai batas tertentu.

Pengungkapan: tidak ada kode di atas yang diuji / dieksekusi tetapi mudah-mudahan harus cukup baik untuk menyampaikan konsep; juga saya sangat menghargai komentar untuk koreksi seperti itu :)


1
Bukankah seharusnya begitu return n * factorial(n-1);?
jinawee

@jinawee, terima kasih telah menunjukkan, saya telah memperbaikinya darin * (n-1)
biksu tua

10

Pemrograman Fungsional adalah bentuk pemrograman deklaratif, yang menggambarkan logika perhitungan dan urutan eksekusi sepenuhnya tidak ditekankan.

Masalah: Saya ingin mengubah makhluk ini dari kuda menjadi jerapah.

  • Perpanjang leher
  • Perpanjang kaki
  • Terapkan tempat
  • Beri makhluk itu lidah hitam
  • Hapus ekor kuda

Setiap item dapat dijalankan dalam urutan apa pun untuk menghasilkan hasil yang sama.

Pemrograman Imperatif bersifat prosedural. Negara dan ketertiban itu penting.

Masalah: Saya ingin memarkir mobil saya.

  1. Perhatikan keadaan awal pintu garasi
  2. Hentikan mobil di jalan masuk
  3. Jika pintu garasi tertutup, buka pintu garasi, ingat keadaan baru; jika tidak, lanjutkan
  4. Tarik mobil ke garasi
  5. Tutup pintu garasi

Setiap langkah harus dilakukan untuk mencapai hasil yang diinginkan. Menarik ke garasi saat pintu garasi ditutup akan menghasilkan pintu garasi yang rusak.


Saya hanya melihat perbedaan dalam sinkronisasi async vs.
Vladimir Vukanac

@VladimirVukanac async / sync adalah mekanisme, bukan bentuk pemrograman
Jakub Keller

2
Oh, terima kasih, saya akan meneliti lebih lanjut tentang itu. Apakah Anda akan berbaik hati, untuk memperbarui masalah 1 agar sama dengan masalah 2 "Saya ingin memarkir mobil saya" tetapi ditulis dengan cara pemrograman fungsional? Maka paralelisme akan dikecualikan.
Vladimir Vukanac

6

Pemrograman fungsional adalah "pemrograman dengan fungsi," di mana suatu fungsi memiliki beberapa sifat matematika yang diharapkan, termasuk transparansi referensial. Dari sifat-sifat ini, sifat-sifat selanjutnya mengalir, khususnya langkah-langkah penalaran akrab yang dimungkinkan oleh substitusi yang mengarah pada bukti matematis (yaitu membenarkan kepercayaan pada hasil).

Oleh karena itu program fungsional hanyalah ekspresi.

Anda dapat dengan mudah melihat kontras antara kedua gaya dengan mencatat tempat-tempat dalam program imperatif di mana ekspresi tidak lagi transparan secara referensial (dan karena itu tidak dibangun dengan fungsi dan nilai, dan tidak dapat dengan sendirinya menjadi bagian dari fungsi). Dua tempat yang paling jelas adalah: mutasi (misalnya variabel) efek samping lain aliran kontrol non-lokal (misalnya pengecualian)

Pada kerangka kerja program-sebagai-ekspresi yang terdiri dari fungsi dan nilai-nilai ini, dibangun paradigma praktis seluruh bahasa, konsep, "pola fungsional", kombinator, dan berbagai jenis sistem dan algoritma evaluasi.

Dengan definisi paling ekstrem, hampir semua bahasa — bahkan C atau Java — dapat disebut fungsional, tetapi biasanya orang mencadangkan istilah untuk bahasa dengan abstraksi yang relevan secara spesifik (seperti penutupan, nilai yang tidak dapat diubah, dan alat bantu sintaksis seperti pencocokan pola). Sejauh penggunaan pemrograman fungsional menyangkut itu melibatkan penggunaan functins dan membangun kode tanpa efek samping. digunakan untuk menulis bukti


3

Gaya pemrograman imperatif dipraktikkan dalam pengembangan web dari tahun 2005 hingga 2013.

Dengan pemrograman imperatif, kami menulis kode yang mencantumkan apa yang seharusnya dilakukan aplikasi kami, langkah demi langkah.

Gaya pemrograman fungsional menghasilkan abstraksi melalui cara pintar menggabungkan fungsi.

Ada disebutkan pemrograman deklaratif dalam jawaban dan mengenai hal itu saya akan mengatakan bahwa pemrograman deklaratif mencantumkan beberapa aturan yang harus kita ikuti. Kami kemudian memberikan apa yang kami sebut sebagai beberapa kondisi awal untuk aplikasi kami dan kami membiarkan aturan-aturan semacam itu mendefinisikan bagaimana aplikasi itu berlaku.

Sekarang, deskripsi singkat ini mungkin tidak masuk akal, jadi mari kita melihat perbedaan antara pemrograman imperatif dan deklaratif dengan menelusuri analogi.

Bayangkan kita tidak membangun perangkat lunak, tetapi kita membuat kue untuk mencari nafkah. Mungkin kita adalah tukang roti yang buruk dan tidak tahu cara membuat kue yang lezat seperti yang seharusnya.

Jadi bos kita memberi kita daftar arah, apa yang kita ketahui sebagai resep.

Resepnya akan memberi tahu kami cara membuat pai. Satu resep ditulis dengan gaya imperatif seperti:

  1. Campurkan 1 cangkir tepung
  2. Tambahkan 1 butir telur
  3. Tambahkan 1 cangkir gula
  4. Tuang adonan ke dalam wajan
  5. Masukkan wajan ke dalam oven selama 30 menit dan 350 derajat F.

Resep deklaratif akan melakukan hal berikut:

1 cangkir tepung, 1 telur, 1 cangkir gula - keadaan awal

Aturan

  1. Jika semuanya tercampur, masukkan ke dalam wajan.
  2. Jika semuanya tidak dicampur, taruh dalam mangkuk.
  3. Jika semuanya ada di dalam panci, letakkan dalam oven.

Jadi pendekatan imperatif ditandai dengan pendekatan langkah demi langkah. Anda mulai dengan langkah pertama dan lanjutkan ke langkah 2 dan seterusnya.

Anda akhirnya berakhir dengan beberapa produk akhir. Jadi membuat pai ini, kami mengambil bahan-bahan ini mencampurnya, menaruhnya di dalam panci dan di oven dan Anda mendapatkan produk akhir Anda.

Dalam dunia deklaratif, ini berbeda. Dalam resep deklaratif kita akan memisahkan resep kita menjadi dua bagian yang terpisah, mulai dengan satu bagian yang mencantumkan status awal resep, seperti variabel. Jadi variabel kami di sini adalah jumlah bahan dan jenisnya.

Kami mengambil kondisi awal atau bahan awal dan menerapkan beberapa aturan padanya.

Jadi kita mengambil kondisi awal dan melewati mereka melalui aturan ini berulang-ulang sampai kita siap untuk makan pai stroberi rhubarb atau apa pun.

Jadi, dalam pendekatan deklaratif, kita harus tahu cara menyusun aturan ini dengan benar.

Jadi aturan yang kita mungkin ingin memeriksa bahan atau negara kita, jika dicampur, masukkan ke dalam panci.

Dengan keadaan awal kami, itu tidak cocok karena kami belum mencampur bahan kami.

Jadi aturan 2 mengatakan, jika tidak tercampur maka campurlah dalam mangkuk. Oke ya aturan ini berlaku.

Sekarang kami memiliki semangkuk bahan campuran sebagai keadaan kami.

Sekarang kita menerapkan negara baru itu ke aturan kita lagi.

Jadi aturan 1 mengatakan jika bahan dicampur tempatkan mereka dalam panci, oke ya sekarang aturan 1 berlaku, mari kita lakukan.

Sekarang kita memiliki keadaan baru ini di mana bahan dicampur dan dalam wajan. Aturan 1 tidak lagi relevan, aturan 2 tidak berlaku.

Aturan 3 mengatakan jika bahan-bahannya ada di dalam wajan, letakkan di dalam oven, bagus aturan yang berlaku untuk keadaan baru ini, mari kita lakukan.

Dan kita berakhir dengan pai apel panas yang lezat atau apa pun.

Sekarang, jika Anda seperti saya, Anda mungkin berpikir, mengapa kita tidak melakukan pemrograman imperatif. Ini masuk akal.

Ya, untuk aliran sederhana ya, tetapi sebagian besar aplikasi web memiliki aliran lebih kompleks yang tidak dapat ditangkap dengan baik oleh desain pemrograman imperatif.

Dalam pendekatan deklaratif, kita mungkin memiliki beberapa bahan awal atau keadaan awal seperti textInput=“” , variabel tunggal.

Mungkin input teks dimulai sebagai string kosong.

Kami mengambil status awal ini dan menerapkannya pada seperangkat aturan yang ditentukan dalam aplikasi Anda.

  1. Jika pengguna memasukkan teks, perbarui input teks. Ya, sekarang itu tidak berlaku.

  2. Jika template diberikan, hitung widget.

  3. Jika input teks diperbarui, render kembali templat tersebut.

Yah, semua ini tidak berlaku sehingga program hanya akan menunggu sebuah peristiwa terjadi.

Jadi pada titik tertentu pengguna memperbarui input teks dan kemudian kita mungkin menerapkan aturan nomor 1.

Kami dapat memperbarui itu ke “abcd”

Jadi kami baru saja memperbarui pembaruan teks dan input teks, aturan nomor 2 tidak berlaku, aturan nomor 3 mengatakan jika input teks diperbarui, yang baru saja terjadi, lalu render ulang templat, lalu kami kembali ke aturan 2 yang mengatakan jika templat diberikan , hitung widgetnya, oke mari kita hitung widgetnya.

Secara umum, sebagai programmer, kami ingin mengusahakan desain pemrograman yang lebih deklaratif.

Imperatif tampak lebih jelas dan jelas, tetapi pendekatan deklaratif berskala sangat baik untuk aplikasi yang lebih besar.


2

• Bahasa Imperatif:

  • Eksekusi yang efisien

  • Semantik yang kompleks

  • Sintaks yang kompleks

  • Concurrency dirancang oleh programmer

  • Pengujian kompleks, tidak memiliki transparansi referensial, memiliki efek samping

  • Memiliki keadaan

• Bahasa Fungsional:

  • Semantik sederhana

  • Sintaks sederhana

  • Eksekusi kurang efisien

  • Program dapat dibuat secara bersamaan

  • Pengujian sederhana, memiliki transparansi referensial, tidak memiliki efek samping

  • Tidak memiliki keadaan

1

Saya pikir itu mungkin untuk mengekspresikan pemrograman fungsional dengan cara imperatif:

  • Menggunakan banyak pemeriksaan keadaan objek dan if... else/ switchpernyataan
  • Beberapa mekanisme timeout / wait untuk menjaga asynchornousness

Ada masalah besar dengan pendekatan seperti itu:

  • Aturan / prosedur diulang
  • Statefulness meninggalkan peluang untuk efek samping / kesalahan

Pemrograman fungsional, mengobati fungsi / metode seperti objek dan merangkul kewarganegaraan, lahir untuk menyelesaikan masalah yang saya percaya.

Contoh penggunaan: aplikasi frontend seperti Android, iOS atau logika aplikasi web termasuk. komunikasi dengan backend.

Tantangan lain ketika mensimulasikan pemrograman fungsional dengan kode imperatif / prosedural:

  • Kondisi balapan
  • Kombinasi kompleks dan urutan acara. Misalnya, pengguna mencoba mengirim uang dalam aplikasi perbankan. Langkah 1) Lakukan semua hal berikut secara paralel, hanya lanjutkan jika semuanya baik a) Periksa apakah pengguna masih baik (penipuan, AML) b) periksa apakah pengguna memiliki saldo yang cukup c) Periksa apakah penerima valid dan baik (penipuan, AML) dll. Langkah 2) melakukan operasi transfer. Langkah 3) Tampilkan pembaruan pada saldo pengguna dan / atau semacam pelacakan. Dengan RxJava misalnya, kode ini ringkas dan masuk akal. Tanpa itu, saya bisa membayangkan akan ada banyak kode, kode berantakan dan rentan kesalahan

Saya juga percaya bahwa pada akhirnya, kode fungsional akan diterjemahkan ke dalam kode perakitan atau mesin yang sangat penting / prosedural oleh kompiler. Namun, kecuali Anda menulis rakitan, sebagai manusia yang menulis kode dengan bahasa tingkat tinggi / dapat dibaca manusia, pemrograman fungsional adalah cara ekspresi yang lebih tepat untuk skenario yang tercantum


-1

Saya tahu pertanyaan ini lebih lama dan yang lain sudah menjelaskannya dengan baik, saya ingin memberikan contoh masalah yang menjelaskan hal yang sama secara sederhana.

Masalah: Menulis tabel 1's.

Solusi: -

Menurut gaya Imperatif: =>

    1*1=1
    1*2=2
    1*3=3
    .
    .
    .
    1*n=n 

Menurut gaya Fungsional: =>

    1
    2
    3
    .
    .
    .
    n

Penjelasan dalam gaya Imperatif kami menulis instruksi secara lebih eksplisit dan yang dapat disebut dengan cara yang lebih sederhana.

Sedangkan seperti dalam gaya Fungsional, hal-hal yang jelas akan diabaikan.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.