Bisakah Anda belajar pemrograman fungsional dalam C? [Tutup]

9

Sebagai hasil dari diskusi komentar di sini , saya ingin tahu apakah Anda dapat mempelajari Pemrograman Fungsional dalam C?

sbi
sumber
30
Terlepas dari apakah Anda bisa atau tidak, Anda seharusnya tidak .
R. Martinho Fernandes
27
Benar! Langkah pertama adalah menulis interpreter Lisp ;-)
Ferruccio
Jika saya sudah benar, pertanyaan itu seharusnya belajar berbagai konsep dengan pseudocode, tanpa setiap bahasa nyata sama sekali.
SK-logic
@ SK-logic: Singkatnya, sangat sedikit pemrogram yang mempelajari pemrograman murni dengan pseudocode, sebagian besar harus mengotori tangan mereka dan wajah mereka ditampar dengan pesan kesalahan dari kompiler / juru bahasa.
sbi
4
@ Sbi, beberapa programmer terbaik telah belajar pemrograman ketika kompiler tidak memiliki pesan kesalahan sama sekali. Pengodean pada kartu punch memerlukan sedikit pemahaman tentang apa yang Anda lakukan jauh sebelum Anda memiliki kesempatan untuk menjalankan kode. Dan tidak perlu disebutkan bahwa dasar untuk pemrograman fungsional telah ditetapkan jauh sebelum komputer pertama dibangun.
SK-logic

Jawaban:

21

Jelas Anda dapat melakukan pemrograman fungsional dalam C. Secara teori, Anda juga dapat mempelajari prinsip-prinsip pemrograman fungsional dalam C, tetapi bahasa tidak membuatnya mudah.

Saya berasumsi Anda memiliki setidaknya sedikit latar belakang dalam OOP; jika Anda melakukannya, Anda harus menyadari bahwa OOP dapat dilakukan dalam C, termasuk polimorfisme, getter / setter, aturan visibilitas, dll., tetapi itu cukup menyakitkan untuk melakukannya, dan Anda perlu tahu baik OOP dan C di dalam- keluar untuk melakukannya. Ini hampir sama dengan FP.

Yang harus Anda lakukan adalah pertama-tama mempelajari bahasa pemrograman fungsional (kebanyakan dari mereka memiliki aturan sintaksis yang sangat sederhana; bukan sintaks yang membuat mereka sulit untuk dipelajari), dan kemudian biarkan kebijaksanaan Anda yang baru didapat memengaruhi cara Anda menulis C.


Sesuai permintaan, beberapa hal yang dapat Anda pelajari dari FP dan kemudian menerapkan, katakanlah, C, C ++ atau Java:

  • Hindari keadaan, terutama keadaan yang bisa berubah yang dibagikan bersama
  • Hargai fungsi murni
  • Hargai evaluasi malas
  • Mampu memodelkan dalam hal kata kerja dan bukan kata benda
  • Mampu mendekati masalah secara rekursif dan iteratif
  • Menggunakan fungsi tingkat tinggi
  • Memikirkan fungsi hanya sebagai jenis nilai lain, yang dapat Anda sampaikan dan kombinasikan dengan nilai-nilai lain
  • Menggunakan fungsi kelas satu sebagai alternatif untuk polimorfisme berbasis objek
tammmer
sumber
1
Bisakah Anda memberikan contoh spesifik tentang bagaimana programmer C / C ++ / Java dapat mengambil manfaat dari kebijaksanaan LISP. Secara teori masuk akal, tetapi saya mencari sesuatu yang konkret.
Pekerjaan
@ Pekerjaan, manfaat nyata apa yang bisa didapat? Itu memperluas pikiran mereka dan mungkin membuat mereka berpikir tentang keadaan yang bisa berubah yang berlebihan sebagai hal yang buruk, apa lagi yang bisa Anda minta?
dan_waterworth
Jadi dapatkah saya menyimpulkan bahwa, walaupun dimungkinkan untuk belajar (sebagian) FP dalam C, tidak masuk akal untuk melakukan itu?
sbi
@Job: Saya telah belajar FP ketika saya belajar LISP sebagai mahasiswa bertahun-tahun yang lalu. Sekitar satu dekade kemudian saya menerapkan FP dalam meta-programming template.
sbi
1
@sbi, cara favorit saya mempelajari bahasa dan konsep baru adalah melalui penerapannya. Dan C adalah bahasa yang cukup baik untuk mengimplementasikan kompiler. Jadi, ya, Anda bisa belajar FP dengan C.
SK-logic
11

C dapat diretas untuk menawarkan beberapa konsep fungsional:

Pertanyaan StackOverflow ini akan memberi tahu Anda lebih banyak. Tetapi meskipun tampaknya mungkin untuk melakukan pemrograman fungsional (atau sebagian besar) di C, peretasan dan ekstensi kompiler dan apa pun yang bukan cara terbaik untuk mempelajari suatu konsep.

Untuk benar - benar mempelajari pemrograman fungsional, taruhan terbaik Anda adalah salah satu bahasa pemrograman fungsional terkemuka seperti Lisp dan dialek-dialeknya ( Clojure , Scheme ), Erlang , dan Haskell . Salah satunya adalah alat yang sempurna yang bekerja dalam pola pikir pemrograman fungsional. F # juga merupakan kandidat yang baik jika Anda memiliki latar belakang. Net, tetapi ini adalah bahasa multi-paradigma, bukan sepenuhnya bahasa pemrograman fungsional.


Seperti yang dicatat para komentar dalam komentar:

Sebenarnya, LISP, clojure dan skema juga multi-paradigma; Haskell, meskipun murni dan malas-bawaan, juga memungkinkan pemrograman imperatif saat berada dalam konteks monadik, dan Haskell memiliki dukungan luas untuk pemrosesan bersamaan. Semua ini memiliki mekanisme yang menerapkan sebagian besar kearifan yang terkumpul di dunia OOP - enkapsulasi, pewarisan, tanggung jawab tunggal, komposisi, dll. Ini bukan soal apakah suatu bahasa MENGIZINKAN paradigma lain; ini tentang paradigma mana yang membentuk titik awal suatu bahasa.

Sepengetahuan saya, Lisp dan dialek-dialeknya serta Erlang adalah kandidat yang lebih baik daripada F # karena mereka mendorong pemrograman fungsional di atas paradigma lain, apa yang dinyatakan oleh para pembuat indah sebagai titik awal bahasa . F # mencakup pemrograman fungsional tetapi tidak mendorongnya atas paradigma yang didukung lainnya, pemrograman imperatif dan oo.

yannis
sumber
Mengenai kalimat terakhir Anda: Dalam hal ini saya berpendapat bahwa F # adalah kandidat yang lebih baik karena tidak akan menjebak Anda dalam pola pikir fungsional. Dalam bahasa multi-paradigma, saat Anda mulai memalu sekrup, Anda bisa langsung beralih ke obeng. Saat terjebak dalam paradigma tunggal Anda mungkin kehilangan perbedaan antara sekrup dan paku.
R. Martinho Fernandes
Nah pertanyaannya adalah tentang cara belajar pemrograman fungsional, bukan pemrograman. Saya berasumsi bahwa op memiliki beberapa pengalaman dalam bahasa multi-paradigma dan menginginkan cara yang paling efisien untuk mempelajari pemrograman fungsional, dalam hal ini F # adalah kandidat yang baik tetapi bukan yang sempurna. Terperangkap dalam paradigma tunggal jelas merupakan cara terbaik ketika Anda ingin belajar dari paradigma tunggal karena Anda tidak harus berurusan dengan segala sesuatu yang bukan itu.
yannis
Saya percaya bahwa belajar ketika paradigma itu sangat cocok dan ketika itu tidak cocok untuk tugas tertentu adalah bagian dari mempelajarinya, mungkin yang paling penting.
R. Martinho Fernandes
4
Sebenarnya, LISP, clojure dan skema juga multi-paradigma; Haskell, meskipun murni dan malas-bawaan, juga memungkinkan pemrograman imperatif sementara dalam konteks monadik, dan Haskell memiliki dukungan luas untuk pemrosesan bersamaan. Semua ini memiliki mekanisme yang menerapkan sebagian besar kearifan yang terkumpul di dunia OOP - enkapsulasi, pewarisan, tanggung jawab tunggal, komposisi, dll. Ini bukan soal apakah suatu bahasa MENGIZINKAN paradigma lain; ini tentang paradigma mana yang membentuk titik awal suatu bahasa.
Pelaku
2
@MartinhoFernandes: Orang bisa berpendapat bahwa terjebak dalam paradigma tunggal adalah cara sempurna untuk belajar ketika itu benar-benar menyebalkan :-)
Jörg W Mittag
2

Anda tidak dapat mempelajari semua aspek pemrograman fungsional dalam C. Tetapi tentunya Anda dapat memulai pemrograman gaya fungsional dengan bahasa imperatif apa pun. Bit-bit awal ini adalah- "Cara menjaga hal-hal tetap murni saat pemrograman." Dan itu bisa dilakukan C juga. Lihat posting blog ini untuk detail-

http://www.johndcook.com/blog/2011/07/24/get-started-functional-programming/

Gulshan
sumber
2

TL; DR

Pemrograman fungsional adalah tentang penutupan dan aplikasinya. Kecuali seseorang dapat menunjukkan kepada Anda perpustakaan penutupan keturunan untuk C, lupakan menggunakan C untuk mempelajari pemrograman fungsional.

Apa itu pemrograman fungsional?

Konsep utama pemrograman fungsional adalah gagasan penutupan yang secara kasar, menangkap fungsi bersama dengan variabel binding. Selain penggunaan penutupan yang luas, ada beberapa ciri khas lain dalam pemrograman fungsional, seperti penggunaan fungsi rekursif dan nilai-nilai yang tidak berubah (keduanya bermain bersama dengan baik). Ciri-ciri ini lebih merupakan masalah budaya daripada yang lainnya, dan tidak ada halangan teknis untuk menggunakannya dalam hampir semua bahasa, ini sebabnya saya fokus pada penutupan dalam jawaban saya: tidak setiap bahasa memungkinkan untuk dengan mudah membuat penutupan.

Tiga ilustrasi kegunaan penutupan

Penggunaan khas penutupan adalah penerapan mekanisme privasi. Misalnya kode Javascript - dalam contoh saya memilih Javascript karena merupakan bahasa fungsional dengan apa yang disebut "sintaks mirip C" dan pertanyaan Anda menunjukkan bahwa Anda terbiasa dengan C:

create_counter = function()
{
  var x = 0;
  var counter = function()
  {
    ++x;
    return x;
  };
  return counter;
}

Lalu dengan

a = create_counter();
b = create_counter();

kami memiliki dua fungsi adan bmenghitung koleksi yang terpisah. Inti dari contoh ini adalah bahwa variabel xditangkap oleh closure yang mendefinisikan counterclosure dan setiap kali x` counterclosure baru is instantiated by the function, it gets its fresh own idea of what.

Penggunaan khas penutupan lainnya adalah definisi aplikasi fungsi secara parsial. Anggaplah bahwa kita memiliki fasilitas pelaporan yang mirip dengan syslogmengimplementasikan suatu fungsi

var log = function(priority, message) {

};

di mana argumen prioritydan messagediharapkan menjadi string, yang pertama menjadi salah satu "debug", "info"dan sebagainya. Kita dapat mendefinisikan pabrik log seperti ini:

var logWithPriority = function(priority) {
  return function(message) {
    log(priority, message);
  };
};

dan menggunakannya untuk menentukan versi khusus dari fasilitas log kami:

var debug = logWithPriority("debug");
var info = logWithPriority("info");

Ini sangat berguna, karena alih-alih menulis kesalahan-cenderung- forseperti ini

for(i = 0; i < journal.length; ++i) {
   log("info", journal[i]);
}

kita dapat menulis pembersih, lebih pendek dan lebih sederhana (tidak ada i, itu jauh lebih baik):

journal.forEach(logWithPriority("info"));

Bidang aplikasi penutupan ketiga yang penting adalah penerapan evaluasi malas - perhatikan bahwa dukungan bahasa khusus dapat memberikan implementasi yang lebih baik.

Fungsi malas, alih-alih melakukan perhitungan langsung, mengembalikan penutupan yang dapat disebut (atau "dipaksa" dalam jargon kemalasan) untuk melakukan pertanyaan. Motivasi untuk melakukan ini adalah bahwa ia memisahkan persiapan perhitungan dan melakukan perhitungan. Contoh praktis dari hal ini adalah kompilasi ekspresi reguler: jika suatu program mengkompilasi banyak ekspresi reguler pada saat startup, itu akan membutuhkan banyak waktu untuk memulai. Jika sebaliknya kita malas menyusun ekspresi reguler dan memaksanya sesuai kebutuhan, maka program kita dapat dengan cepat memulai. Tentu saja, ekspresi reguler dapat diganti di sini dengan struktur apa pun yang memerlukan waktu inisialisasi yang cukup besar.

Berikut adalah cara menerapkan evaluasi malas dengan penutupan. Pertimbangkan implementasi klasik fungsi arrayMax mengembalikan max dalam sebuah array:

function arrayMax(array) {
  return array.reduce(function(a, b) {
    return Math.min(a, b);
  };
}

Varian yang malas adalah:

function arrayMax(array) {
  var memo = null;
  function actuallyCompute() {
    if(memo === null) {
      memo = array.reduce(function(a, b) {
        return Math.min(a, b);
      });
    }
    return memo;
  }
  return actuallyCompute;
}

Nilai yang dikembalikan adalah penutupan yang dapat digunakan untuk menghitung nilai atau mengambilnya di lain waktu jika telah dihitung.

Dengan ketiga contoh ini, kita harus yakin bahwa penutupan dan aplikasinya adalah inti dari pemrograman fungsional.

Kesimpulan

Mempelajari pemrograman fungsional berarti mempelajari cara memprogram dengan penutupan. Sebagai konsekuensinya, bahasa memungkinkan manipulasi penutupan yang mudah, dan terutama penerapan sebagian fungsi, harus dipertimbangkan ketika mencari bahasa untuk mempelajari pemrograman fungsional. Sebaliknya, bahasa di mana penutupan tidak dapat dengan mudah dimanipulasi akan menjadi pilihan yang buruk.

Michael Le Barbier Grünewald
sumber
1
Terima kasih atas sampel yang bagus ini. BTW, saat Anda mendefinisikan var info, bukankah itu journal.forEach (info)?
ejaenv
Ya, itu akan menjadi varian yang sesuai! :)
Michael Le Barbier Grünewald
1

Saya pikir alat yang Anda gunakan sangat memengaruhi belajar Anda. Hampir mustahil untuk mempelajari konsep pemrograman yang bahasa pemrogramannya Anda gunakan tidak menyediakan sarana untuk memanfaatkannya. Tentu, Anda selalu dapat belajar beberapa hal, tetapi Anda tidak dapat mempelajarinya dengan benar.

Tapi itu tetap akademik, karena, seperti dikatakan Martinho dalam komentarnya , bahkan jika Anda bisa belajar pemrograman fungsional, Anda tidak boleh mencoba melakukan itu, karena ada bahasa di mana ini jauh lebih mudah.

sbi
sumber
1

Anda seharusnya tidak belajar pemrograman fungsional dalam C, tetapi dalam bahasa fungsional yang ketat (Haskell, Caml, Erlang, dll.).

Jika Anda baru mengenal fungsional, Anda tidak akan pernah mendapatkannya dengan bahasa yang tidak fungsional. Kemungkinan besar, Anda akan melatih diri untuk melakukan apa yang menurut Anda pemrograman fungsional dan mempelajari berbagai hal dengan cara yang salah. Dan itu selalu lebih sulit untuk «mempelajari kembali» hal-hal dengan cara yang benar daripada mempelajarinya dengan cara yang benar pada awalnya.

Bagaimanapun, saya pikir melakukan fungsional dalam C adalah latihan yang baik untuk seseorang yang sudah tahu fungsional. Karena orang itu akan mempelajari apa yang terjadi di balik tudung - apa yang sebenarnya dilakukan komputer.

deadalnix
sumber