Saya telah membaca artikel Wikipedia untuk pemrograman prosedural dan pemrograman fungsional , tetapi saya masih sedikit bingung. Bisakah seseorang merebusnya sampai ke inti?
Saya telah membaca artikel Wikipedia untuk pemrograman prosedural dan pemrograman fungsional , tetapi saya masih sedikit bingung. Bisakah seseorang merebusnya sampai ke inti?
Jawaban:
Bahasa fungsional (idealnya) memungkinkan Anda untuk menulis fungsi matematika, yaitu fungsi yang mengambil n argumen dan mengembalikan nilai. Jika program dijalankan, fungsi ini dievaluasi secara logis sesuai kebutuhan. 1
Bahasa prosedural, di sisi lain, melakukan serangkaian langkah berurutan . (Ada cara mengubah logika sekuensial menjadi logika fungsional yang disebut gaya kelanjutan lewat .)
Sebagai konsekuensinya, program murni fungsional selalu menghasilkan nilai yang sama untuk suatu input, dan urutan evaluasi tidak didefinisikan dengan baik; yang berarti bahwa nilai tidak pasti seperti input pengguna atau nilai acak sulit untuk dimodelkan dalam bahasa yang murni fungsional.
1 Seperti semua hal lain dalam jawaban ini, itu adalah generalisasi. Properti ini, mengevaluasi perhitungan ketika hasilnya dibutuhkan daripada secara berurutan di mana ia disebut, dikenal sebagai "kemalasan". Tidak semua bahasa fungsional sebenarnya universal malas, juga kemalasan tidak terbatas pada pemrograman fungsional. Sebaliknya, deskripsi yang diberikan di sini memberikan "kerangka mental" untuk berpikir tentang gaya pemrograman yang berbeda yang bukan kategori yang berbeda dan berlawanan melainkan ide-ide yang mengalir.
Pada dasarnya kedua gaya itu, seperti Yin dan Yang. Satu terorganisir, sementara yang lain kacau. Ada situasi ketika pemrograman Fungsional adalah pilihan yang jelas, dan situasi lain pemrograman Prosedural adalah pilihan yang lebih baik. Inilah sebabnya mengapa setidaknya ada dua bahasa yang baru-baru ini keluar dengan versi baru, yang mencakup kedua gaya pemrograman. ( Perl 6 dan D 2 )
sub factorial ( UInt:D $n is copy ) returns UInt {
# modify "outside" state
state $call-count++;
# in this case it is rather pointless as
# it can't even be accessed from outside
my $result = 1;
loop ( ; $n > 0 ; $n-- ){
$result *= $n;
}
return $result;
}
int factorial( int n ){
int result = 1;
for( ; n > 0 ; n-- ){
result *= n;
}
return result;
}
(disalin dari Wikipedia );
fac :: Integer -> Integer
fac 0 = 1
fac n | n > 0 = n * fac (n-1)
atau dalam satu baris:
fac n = if n > 0 then n * fac (n-1) else 1
proto sub factorial ( UInt:D $n ) returns UInt {*}
multi sub factorial ( 0 ) { 1 }
multi sub factorial ( $n ) { $n * samewith $n-1 } # { $n * factorial $n-1 }
pure int factorial( invariant int n ){
if( n <= 1 ){
return 1;
}else{
return n * factorial( n-1 );
}
}
Faktorial sebenarnya adalah contoh umum untuk menunjukkan betapa mudahnya membuat operator baru di Perl 6 dengan cara yang sama seperti Anda membuat subrutin. Fitur ini sudah tertanam dalam Perl 6 sehingga sebagian besar operator dalam implementasi Rakudo didefinisikan dengan cara ini. Hal ini juga memungkinkan Anda untuk menambahkan kandidat multi Anda sendiri ke operator yang ada.
sub postfix:< ! > ( UInt:D $n --> UInt )
is tighter(&infix:<*>)
{ [*] 2 .. $n }
say 5!; # 120
Contoh ini juga memperlihatkan pembuatan rentang ( 2..$n
) dan meta-operator pengurangan daftar ( [ OPERATOR ] LIST
) yang dikombinasikan dengan operator perkalian infiks numerik. ( *
)
Ini juga menunjukkan bahwa Anda dapat memasukkan --> UInt
tanda tangan alih-alih returns UInt
setelahnya.
(Anda bisa lolos dari memulai rentang dengan 2
sebagai "operator" kelipatan akan kembali 1
ketika dipanggil tanpa argumen)
sub postfix:<!> ($n) { [*] 1..$n }
No operation can have side effects
Anda menjelaskannya?
sub foo( $a, $b ){ ($a,$b).pick }
← tidak selalu mengembalikan output yang sama untuk input yang sama, sedangkan yang berikut tidaksub foo( $a, $b ){ $a + $b }
Saya belum pernah melihat definisi ini diberikan di tempat lain, tetapi saya pikir ini meringkas perbedaan yang diberikan di sini dengan cukup baik:
Pemrograman fungsional berfokus pada ekspresi
Pemrograman prosedural berfokus pada pernyataan
Ekspresi memiliki nilai. Program fungsional adalah ekspresi yang nilainya adalah urutan instruksi yang dapat dilakukan komputer.
Pernyataan tidak memiliki nilai dan sebagai gantinya mengubah keadaan beberapa mesin konseptual.
Dalam bahasa murni fungsional tidak akan ada pernyataan, dalam arti bahwa tidak ada cara untuk memanipulasi negara (mereka mungkin masih memiliki konstruksi sintaksis bernama "pernyataan", tetapi kecuali jika memanipulasi keadaan saya tidak akan menyebutnya pernyataan dalam pengertian ini ). Dalam bahasa yang murni prosedural tidak akan ada ekspresi, semuanya akan menjadi instruksi yang memanipulasi keadaan mesin.
Haskell akan menjadi contoh bahasa murni fungsional karena tidak ada cara untuk memanipulasi keadaan. Kode mesin akan menjadi contoh bahasa yang murni prosedural karena segala sesuatu dalam suatu program adalah pernyataan yang memanipulasi keadaan register dan memori mesin.
Bagian membingungkan adalah bahwa sebagian besar bahasa pemrograman mengandung kedua ekspresi dan pernyataan, yang memungkinkan Anda untuk mencampur paradigma. Bahasa dapat diklasifikasikan sebagai lebih fungsional atau lebih prosedural berdasarkan seberapa banyak mereka mendorong penggunaan pernyataan vs ekspresi.
Sebagai contoh, C akan lebih fungsional daripada COBOL karena panggilan fungsi adalah ekspresi, sedangkan memanggil sub program di COBOL adalah pernyataan (yang memanipulasi keadaan variabel bersama dan tidak mengembalikan nilai). Python akan lebih fungsional daripada C karena memungkinkan Anda untuk mengekspresikan logika kondisional sebagai ekspresi menggunakan evaluasi hubung singkat (test && path1 || path2 sebagai lawan pernyataan if). Skema akan lebih fungsional daripada Python karena semua yang ada dalam skema adalah ekspresi.
Anda masih dapat menulis dengan gaya fungsional dalam bahasa yang mendorong paradigma prosedural dan sebaliknya. Lebih sulit dan / atau lebih canggung untuk menulis dalam paradigma yang tidak didukung oleh bahasa.
Dalam ilmu komputer, pemrograman fungsional adalah paradigma pemrograman yang memperlakukan komputasi sebagai evaluasi fungsi matematika dan menghindari keadaan dan data yang bisa berubah. Ini menekankan penerapan fungsi, berbeda dengan gaya pemrograman prosedural yang menekankan perubahan status.
GetUserContext()
fungsi, konteks pengguna akan diteruskan. Apakah ini pemrograman fungsional? Terima kasih sebelumnya.
Saya percaya bahwa pemrograman prosedural / fungsional / obyektif adalah tentang bagaimana mendekati suatu masalah.
Gaya pertama akan merencanakan semuanya menjadi langkah, dan menyelesaikan masalah dengan menerapkan satu langkah (prosedur) pada suatu waktu. Di sisi lain, pemrograman fungsional akan menekankan pendekatan divide-and-conquer, di mana masalah dibagi menjadi sub-masalah, maka setiap sub-masalah diselesaikan (membuat fungsi untuk menyelesaikan sub masalah itu) dan hasilnya digabungkan ke buat jawaban untuk seluruh masalah. Terakhir, pemrograman Objective akan meniru dunia nyata dengan membuat dunia-mini di dalam komputer dengan banyak objek, yang masing-masing memiliki (agak) karakteristik unik, dan berinteraksi dengan yang lain. Dari interaksi tersebut, hasilnya akan muncul.
Setiap gaya pemrograman memiliki kelebihan dan kelemahannya sendiri. Oleh karena itu, melakukan sesuatu seperti "pemrograman murni" (yaitu murni prosedural - tidak ada yang melakukan ini, omong-omong, yang agak aneh - atau murni fungsional atau murni objektif) sangat sulit, jika bukan tidak mungkin, kecuali beberapa masalah dasar khusus dirancang untuk menunjukkan keunggulan gaya pemrograman (karenanya, kami menyebut mereka yang menyukai kemurnian "weenie": D).
Kemudian, dari gaya tersebut, kami memiliki bahasa pemrograman yang dirancang untuk dioptimalkan untuk beberapa gaya masing-masing. Misalnya, Majelis semua tentang prosedural. Oke, sebagian besar bahasa awal bersifat prosedural, bukan hanya ASM, seperti C, Pascal, (dan Fortran, saya dengar). Kemudian, kita memiliki semua Java yang terkenal di sekolah objektif (Sebenarnya, Java dan C # juga berada dalam kelas yang disebut "berorientasi uang," tetapi itu perlu dibahas lagi). Juga objektif adalah Smalltalk. Di sekolah fungsional, kita akan memiliki "hampir fungsional" (beberapa menganggap mereka tidak murni) keluarga Lisp dan keluarga ML dan banyak "murni fungsional" Haskell, Erlang, dll. Omong-omong, ada banyak bahasa umum seperti Perl, Python , Ruby.
num = 1
def function_to_add_one(num):
num += 1
return num
function_to_add_one(num)
function_to_add_one(num)
function_to_add_one(num)
function_to_add_one(num)
function_to_add_one(num)
#Final Output: 2
num = 1
def procedure_to_add_one():
global num
num += 1
return num
procedure_to_add_one()
procedure_to_add_one()
procedure_to_add_one()
procedure_to_add_one()
procedure_to_add_one()
#Final Output: 6
function_to_add_one
adalah suatu fungsi
procedure_to_add_one
adalah sebuah prosedur
Bahkan jika Anda menjalankan fungsi lima kali, setiap kali itu akan kembali 2
Jika Anda menjalankan prosedur lima kali, pada akhir menjalankan kelima akan memberi Anda 6 .
Untuk memperluas komentar Konrad:
Sebagai konsekuensinya, program murni fungsional selalu menghasilkan nilai yang sama untuk suatu input, dan urutan evaluasi tidak didefinisikan dengan baik;
Karena itu, kode fungsional umumnya lebih mudah diparalelkan. Karena (umumnya) tidak ada efek samping dari fungsi, dan mereka (umumnya) hanya bertindak berdasarkan argumen mereka, banyak masalah konkurensi hilang.
Pemrograman fungsional juga digunakan ketika Anda harus mampu membuktikan kode Anda sudah benar. Ini jauh lebih sulit untuk dilakukan dengan pemrograman prosedural (tidak mudah dengan fungsional, tetapi masih lebih mudah).
Penafian: Saya tidak pernah menggunakan pemrograman fungsional selama bertahun-tahun, dan baru saja mulai melihatnya lagi, jadi saya mungkin tidak sepenuhnya benar di sini. :)
Satu hal yang belum saya lihat benar-benar ditekankan di sini adalah bahwa bahasa fungsional modern seperti Haskell benar-benar lebih pada fungsi kelas satu untuk kontrol aliran daripada rekursi eksplisit. Anda tidak perlu mendefinisikan faktorial secara rekursif di Haskell, seperti yang dilakukan di atas. Saya pikir sesuatu seperti
fac n = foldr (*) 1 [1..n]
adalah konstruksi idiomatis yang sempurna, dan jauh lebih dekat dalam roh untuk menggunakan loop daripada menggunakan rekursi eksplisit.
Bahasa prosedural cenderung untuk melacak negara (menggunakan variabel) dan cenderung mengeksekusi sebagai urutan langkah-langkah. Bahasa yang murni fungsional tidak melacak keadaan, menggunakan nilai-nilai yang tidak dapat diubah, dan cenderung dieksekusi sebagai serangkaian dependensi. Dalam banyak kasus, status tumpukan panggilan akan menyimpan informasi yang setara dengan apa yang akan disimpan dalam variabel status dalam kode prosedural.
Rekursi adalah contoh klasik pemrograman gaya fungsional.
Konrad berkata:
Sebagai konsekuensinya, program murni fungsional selalu menghasilkan nilai yang sama untuk suatu input, dan urutan evaluasi tidak didefinisikan dengan baik; yang berarti bahwa nilai tidak pasti seperti input pengguna atau nilai acak sulit untuk dimodelkan dalam bahasa yang murni fungsional.
Urutan evaluasi dalam program yang murni fungsional mungkin sulit (er) untuk alasan tentang (terutama dengan kemalasan) atau bahkan tidak penting tapi saya pikir mengatakan itu tidak didefinisikan dengan baik membuatnya terdengar seperti Anda tidak dapat mengetahui apakah program Anda berjalan bekerja sama sekali!
Mungkin penjelasan yang lebih baik adalah bahwa aliran kontrol dalam program fungsional didasarkan pada ketika nilai argumen fungsi dibutuhkan. Hal Baik tentang hal ini bahwa dalam program yang ditulis dengan baik, status menjadi eksplisit: setiap fungsi mencantumkan inputnya sebagai parameter alih-alih mengubah keadaan global secara sewenang-wenang . Jadi pada tingkat tertentu, lebih mudah untuk berpikir tentang urutan evaluasi sehubungan dengan satu fungsi pada suatu waktu . Setiap fungsi dapat mengabaikan seluruh alam semesta dan fokus pada apa yang perlu dilakukan. Ketika dikombinasikan, fungsi dijamin bekerja sama [1] seperti yang mereka lakukan secara terpisah.
... nilai tidak pasti seperti input pengguna atau nilai acak sulit untuk dimodelkan dalam bahasa yang murni fungsional.
Solusi untuk masalah input dalam program murni fungsional adalah dengan menanamkan bahasa imperatif sebagai DSL menggunakan abstraksi yang cukup kuat . Dalam bahasa imperatif (atau non-murni fungsional) ini tidak diperlukan karena Anda dapat "menipu" dan melewati status secara implisit dan urutan evaluasi eksplisit (baik suka atau tidak). Karena ini "curang" dan evaluasi paksa semua parameter untuk setiap fungsi, dalam bahasa imperatif 1) Anda kehilangan kemampuan untuk membuat mekanisme aliran kontrol Anda sendiri (tanpa makro), 2) kode tidak inheren thread aman dan / atau diparalelkan secara default, 3) dan mengimplementasikan sesuatu seperti undo (perjalanan waktu) membutuhkan kerja yang hati-hati (programmer imperatif harus menyimpan resep untuk mendapatkan kembali nilai lama!), Sedangkan pemrograman fungsional murni memberi Anda semua hal ini — dan beberapa lagi saya dapat lupa— "gratis".
Saya harap ini tidak terdengar seperti semangat, saya hanya ingin menambahkan beberapa perspektif. Pemrograman imperatif dan pemrograman paradigma campuran dalam bahasa yang kuat seperti C # 3.0 masih merupakan cara yang sangat efektif untuk menyelesaikan sesuatu dan tidak ada peluru perak .
[1] ... kecuali mungkin dengan menghormati penggunaan memori (lih. Foldl dan foldl 'di Haskell).
Untuk memperluas komentar Konrad:
dan urutan evaluasi tidak didefinisikan dengan baik
Beberapa bahasa fungsional memiliki apa yang disebut Evaluasi Malas. Yang berarti suatu fungsi tidak dieksekusi sampai nilainya dibutuhkan. Sampai saat itu fungsi itu sendiri adalah apa yang diedarkan.
Bahasa prosedural adalah langkah 1 langkah 2 langkah 3 ... jika dalam langkah 2 Anda mengatakan tambahkan 2 + 2, ia melakukannya saat itu juga. Dalam evaluasi malas Anda akan mengatakan menambahkan 2 + 2, tetapi jika hasilnya tidak pernah digunakan, itu tidak pernah melakukan penambahan.
Jika Anda memiliki kesempatan, saya akan merekomendasikan untuk mendapatkan salinan Lisp / Skema, dan melakukan beberapa proyek di dalamnya. Sebagian besar ide yang belakangan ini menjadi kereta musik diungkapkan dalam Lisp dekade lalu: pemrograman fungsional, lanjutan (sebagai penutup), pengumpulan sampah, bahkan XML.
Jadi itu akan menjadi cara yang baik untuk memulai semua ide saat ini, dan beberapa lagi, seperti perhitungan simbolik.
Anda harus tahu untuk apa pemrograman fungsional baik, dan untuk apa tidak baik. Itu tidak baik untuk semuanya. Beberapa masalah paling baik diungkapkan dalam hal efek samping, di mana pertanyaan yang sama memberikan jawaban yang berbeda tergantung pada kapan ditanya.
@Creighton:
Di Haskell ada fungsi perpustakaan yang disebut produk :
prouduct list = foldr 1 (*) list
atau hanya:
product = foldr 1 (*)
jadi faktorial "idiomatik"
fac n = foldr 1 (*) [1..n]
hanya akan
fac n = product [1..n]
Pemrograman prosedural membagi urutan pernyataan dan konstruksi kondisional ke dalam blok terpisah yang disebut prosedur yang diparameterisasi terhadap argumen yang merupakan nilai (non-fungsional).
Pemrograman fungsional adalah sama kecuali bahwa fungsi adalah nilai kelas satu, sehingga mereka dapat diteruskan sebagai argumen ke fungsi lain dan dikembalikan sebagai hasil dari pemanggilan fungsi.
Perhatikan bahwa pemrograman fungsional adalah generalisasi pemrograman prosedural dalam interpretasi ini. Namun, minoritas menafsirkan "pemrograman fungsional" berarti bebas efek samping yang sangat berbeda tetapi tidak relevan untuk semua bahasa fungsional utama kecuali Haskell.
Untuk memahami perbedaannya, kita perlu memahami bahwa paradigma "ayah baptis" pemrograman pemrograman prosedural dan fungsional adalah pemrograman imperatif .
Pada dasarnya pemrograman prosedural hanyalah cara menyusun program-program penting di mana metode utama abstraksi adalah "prosedur." (atau "fungsi" dalam beberapa bahasa pemrograman). Bahkan Pemrograman Berorientasi Objek hanyalah cara lain untuk menyusun program imperatif, di mana negara dirangkum dalam objek, menjadi objek dengan "kondisi saat ini," ditambah objek ini memiliki serangkaian fungsi, metode, dan hal-hal lain yang memungkinkan Anda Programmer memanipulasi atau memperbarui status.
Sekarang, dalam hal pemrograman fungsional, intinya dalam pendekatannya adalah ia mengidentifikasi nilai apa yang harus diambil dan bagaimana nilai-nilai ini harus ditransfer. (jadi tidak ada status, dan tidak ada data yang dapat berubah karena mengambil fungsi sebagai nilai kelas pertama dan meneruskannya sebagai parameter ke fungsi lain).
PS: Memahami setiap paradigma pemrograman digunakan untuk harus menjelaskan perbedaan di antara mereka semua.
PSS: Pada akhirnya, paradigma pemrograman hanyalah pendekatan yang berbeda untuk menyelesaikan masalah.
PSS: jawaban kuora ini punya penjelasan yang bagus.
Tidak ada jawaban di sini yang menunjukkan pemrograman fungsional idiomatik. Jawaban faktorial rekursif sangat bagus untuk mewakili rekursi dalam FP, tetapi sebagian besar kode tidak rekursif jadi saya tidak berpikir jawaban itu sepenuhnya representatif.
Katakanlah Anda memiliki array string, dan setiap string mewakili integer seperti "5" atau "-200". Anda ingin memeriksa larik masukan string ini terhadap kasus uji internal Anda (Menggunakan perbandingan bilangan bulat). Kedua solusi ditunjukkan di bawah ini
arr_equal(a : [Int], b : [Str]) -> Bool {
if(a.len != b.len) {
return false;
}
bool ret = true;
for( int i = 0; i < a.len /* Optimized with && ret*/; i++ ) {
int a_int = a[i];
int b_int = parseInt(b[i]);
ret &= a_int == b_int;
}
return ret;
}
eq = i, j => i == j # This is usually a built-in
toInt = i => parseInt(i) # Of course, parseInt === toInt here, but this is for visualization
arr_equal(a : [Int], b : [Str]) -> Bool =
zip(a, b.map(toInt)) # Combines into [Int, Int]
.map(eq)
.reduce(true, (i, j) => i && j) # Start with true, and continuously && it with each value
Sementara bahasa fungsional murni umumnya adalah bahasa penelitian (Karena dunia nyata menyukai efek samping gratis), bahasa prosedural dunia nyata akan menggunakan sintaksis fungsional yang jauh lebih sederhana bila perlu.
Ini biasanya diimplementasikan dengan perpustakaan eksternal seperti Lodash , atau tersedia built-in dengan bahasa yang lebih baru seperti Rust . Angkat berat dari pemrograman fungsional dilakukan dengan fungsi / konsep seperti map
, filter
, reduce
, currying
, partial
, tiga terakhir yang Anda dapat melihat untuk pemahaman lebih lanjut.
Agar dapat digunakan di alam bebas, kompiler biasanya harus mencari cara untuk mengubah versi fungsional menjadi versi prosedural secara internal, karena overhead panggilan fungsi terlalu tinggi. Kasus rekursif seperti faktorial yang ditampilkan akan menggunakan trik seperti tail call untuk menghapus O (n) penggunaan memori. Fakta bahwa tidak ada efek samping memungkinkan kompiler fungsional untuk mengimplementasikan && ret
optimasi bahkan ketika .reduce
itu dilakukan terakhir. Menggunakan Lodash di JS, jelas tidak memungkinkan untuk optimasi apa pun, jadi ini adalah hit untuk kinerja (Yang biasanya tidak menjadi perhatian dengan pengembangan web). Bahasa seperti Rust akan mengoptimalkan secara internal (Dan memiliki fungsi seperti try_fold
untuk membantu && ret
optimasi).