"Blub paradox" dan c ++

37

Saya sedang membaca artikel di sini: http://www.paulgraham.com/avg.html dan bagian tentang "blub paradox" sangat menarik. Sebagai seseorang yang sebagian besar kode dalam c ++ tetapi memiliki eksposur ke bahasa lain (kebanyakan Haskell) saya menyadari beberapa hal berguna dalam bahasa ini yang sulit untuk ditiru di c ++. Pertanyaannya adalah terutama untuk orang-orang yang mahir dalam c ++ dan beberapa bahasa lainnya, apakah ada fitur bahasa yang kuat atau idiom yang Anda gunakan dalam bahasa yang akan sulit untuk dikonseptualisasikan atau diimplementasikan jika Anda hanya menulis dalam c ++?

Secara khusus kutipan ini menarik perhatian saya:

Dengan induksi, satu-satunya programmer dalam posisi untuk melihat semua perbedaan kekuatan antara berbagai bahasa adalah mereka yang mengerti yang paling kuat. (Ini mungkin apa yang Eric Raymond maksudkan tentang Lisp menjadikan Anda seorang programmer yang lebih baik.) Anda tidak dapat mempercayai pendapat orang lain, karena paradoks Blub: mereka puas dengan bahasa apa pun yang mereka gunakan, karena itu menentukan cara mereka berpikir tentang program.

Jika ternyata saya setara dengan programmer "Blub" berdasarkan penggunaan c ++ ini menimbulkan pertanyaan berikut: Apakah ada konsep atau teknik yang berguna yang Anda temui dalam bahasa lain yang Anda akan merasa sulit dikonsep seandainya Anda telah menulis atau "berpikir" dalam c ++?

Sebagai contoh, paradigma pemrograman logika yang terlihat dalam bahasa seperti Prolog dan Mercury dapat diimplementasikan dalam c ++ menggunakan library castor tetapi pada akhirnya saya menemukan bahwa secara konseptual saya berpikir dalam hal kode Prolog dan menerjemahkan ke c ++ yang setara ketika menggunakan ini. Sebagai cara memperluas pengetahuan pemrograman saya, saya mencoba mencari tahu apakah ada contoh serupa lainnya dari idiom berguna / kuat yang lebih efisien diekspresikan dalam bahasa lain yang mungkin tidak saya sadari sebagai pengembang c ++. Contoh lain yang terlintas dalam pikiran adalah sistem makro di lisp, menghasilkan kode program dari dalam program tampaknya memiliki banyak manfaat untuk beberapa masalah. Ini tampaknya sulit untuk diimplementasikan dan dipikirkan dari dalam c ++.

Pertanyaan ini tidak dimaksudkan untuk menjadi debat "c ++ vs lisp" atau debat jenis perang bahasa apa pun. Mengajukan pertanyaan seperti ini adalah satu-satunya cara saya dapat melihat kemungkinan untuk mencari tahu tentang hal-hal yang saya tidak tahu saya tidak tahu.

antar jemput87
sumber
2
Saya setuju. Selama ini tidak berubah menjadi perdebatan C ++ vs Lisp saya pikir ada sesuatu yang bisa dipelajari di sini.
jeffythedragonslayer
@MasonWheeler: there are things that other languages can do that Lisp can't- Tidak mungkin, karena Lisp adalah Turing-complete. Mungkin Anda bermaksud mengatakan bahwa ada beberapa hal yang tidak praktis dilakukan di Lisp? Saya bisa mengatakan hal yang sama tentang bahasa pemrograman apa pun .
Robert Harvey
2
@RobertHarvey: "Semua bahasa sama kuatnya dalam arti setara dengan Turing, tapi itu bukan arti kata yang dipedulikan pemrogram. (Tidak ada yang mau memprogram mesin Turing.) Jenis daya yang dipedulikan pemrogram mungkin tidak dapat didefinisikan secara formal, tetapi satu cara untuk menjelaskannya adalah dengan mengatakan bahwa itu merujuk pada fitur yang hanya dapat Anda peroleh dalam bahasa yang kurang kuat dengan menulis penerjemah untuk bahasa yang lebih kuat di dalamnya. " - Paul Graham, dalam catatan kaki ke tiang roda yang dimaksud. (Lihat apa yang saya maksud?)
Mason Wheeler
@Mason Wheeler: (Tidak juga.)
Robert Harvey

Jawaban:

16

Ya, karena Anda menyebut Haskell:

  1. Pencocokan Pola. Saya menemukan pencocokan pola menjadi lebih mudah untuk dibaca dan ditulis. Pertimbangkan definisi peta dan pikirkan bagaimana itu akan diterapkan dalam bahasa tanpa pencocokan pola.

    map :: (a -> b) -> [a] -> [b]
    map f [] = []
    map f (x:xs) = f x : map f xs
  2. Sistem tipe. Terkadang hal itu bisa menyakitkan, tetapi ini sangat membantu. Anda harus memprogram untuk benar-benar memahaminya dan berapa banyak bug yang ditangkap. Juga, transparansi referensial sangat bagus. Ini hanya menjadi jelas setelah pemrograman di Haskell untuk sementara waktu berapa banyak bug yang disebabkan oleh pengelolaan negara dalam bahasa imperatif.

  3. Pemrograman fungsional secara umum. Menggunakan peta dan lipatan alih-alih iterasi. Rekursi. Ini tentang berpikir pada level yang lebih tinggi.

  4. Evaluasi malas. Sekali lagi ini tentang berpikir di tingkat yang lebih tinggi dan membiarkan sistem menangani evaluasi.

  5. Kabala, paket, dan modul. Memiliki paket unduhan Cabal bagi saya jauh lebih nyaman daripada menemukan kode sumber, menulis makefile, dll. Mampu mengimpor hanya nama-nama tertentu jauh lebih baik daripada pada dasarnya semua file sumber dibuang bersama kemudian dikompilasi.

Joel Burget
sumber
2
Catatan tentang pencocokan pola: Saya tidak akan mengatakan lebih mudah untuk menulis secara umum, tetapi setelah Anda membaca sedikit tentang masalah ekspresi menjadi jelas bahwa hal-hal seperti jika dan beralih pernyataan, enum dan pola pengamat semua implementasi yang lebih rendah dari Tipe Data Aljabar. + Pencocokan Pola. (Dan mari kita tidak memulai tentang bagaimana Mungkin membuat pengecualian null pointer usang)
hugomg
Apa yang Anda katakan itu benar, tetapi masalah ekspresi adalah tentang batasan tipe data aljabar (dan tentang batasan ganda dari OOP standar).
Blaisorblade
@hugomg Apakah maksud Anda Pola pengunjung alih-alih Pengamat?
Sebastian Redl
iya nih. Saya selalu mengganti kedua nama itu :)
hugomg
@hugomg Ini bukan tentang memiliki Maybe(untuk C ++ lihat std::optional), ini tentang harus secara eksplisit menandai hal-hal sebagai opsional / nullable / mungkin.
Deduplicator
7

Memoize!

Coba tulis dalam C ++. Tidak dengan C ++ 0x.

Terlalu rumit? Oke, coba dengan C ++ 0x.

Lihat apakah Anda dapat mengalahkan versi waktu kompilasi 4-line (atau 5-line, apa pun: P) ini dalam D:

auto memoize(alias Fn, T...)(T args) {
    auto key = tuple(args);                               //Key is all the args
    static typeof(Fn(args))[typeof(key)] cache;           //Hashtable!
    return key in cache ? cache[key] : (cache[key] = Fn(args));
}

Yang perlu Anda lakukan untuk memanggilnya adalah sesuatu seperti:

int fib(int n) { return n > 1 ? memoize!(fib)(n - 1) + memoize!(fib)(n - 2) : 1;}
fib(60);

Anda juga dapat mencoba sesuatu yang serupa di Skema, meskipun sedikit lebih lambat karena terjadi pada saat run time dan karena pencarian di sini adalah linier, bukan hash (dan juga, karena Skema):

(define (memoize f)
    (let ((table (list)))
        (lambda args
            (cdr
                (or (assoc args table)
                    (let ((entry (cons args (apply f args))))
                        (set! table (cons entry table))
                        entry))))))
(define (fib n)
        (if (<= n 1)
            1
            (+ (fib (1- n))
                (fib (- n 2)))))))
(set! fib (memoize fib))
Mehrdad
sumber
1
Jadi Anda suka APL di mana Anda bisa menulis apa pun dalam satu baris? Ukuran tidak masalah!
Bo Persson
@ Menjadi: Saya belum pernah menggunakan APL. Saya tidak yakin apa yang Anda maksud dengan "ukuran tidak masalah", tetapi apakah ada yang salah dengan kode saya yang membuat Anda mengatakan itu? Dan apakah ada beberapa keuntungan untuk bagaimana Anda melakukan ini dalam bahasa yang berbeda (seperti C ++) yang tidak saya sadari? (Saya sedikit mengedit nama variabel, jika itu yang Anda maksudkan.)
Mehrdad
1
@Mehrdad - Komentar saya tentang program paling ringkas bukanlah pertanda bahasa pemrograman terbaik. Dalam hal ini APL akan menang, karena Anda melakukan banyak hal dengan satu operator char. Satu-satunya masalah adalah tidak terbaca.
Bo Persson
@ Menjadi: Seperti yang saya katakan, saya tidak merekomendasikan APL; Aku bahkan belum pernah melihatnya. Ukuran adalah satu kriteria (meskipun yang signifikan, seperti yang dapat dilihat jika Anda mencoba ini dengan C ++) ... tetapi apakah ada yang salah dengan kode ini ?
Mehrdad
1
@ Matt: Kode Anda memoized sebuah fungsi, tetapi kode ini dapat memoize setiap fungsi. Ini sama sekali tidak setara. Jika Anda benar-benar mencoba menulis fungsi tingkat tinggi seperti ini di C ++ 0x, itu jauh lebih membosankan daripada di D (meskipun masih sangat mungkin ... meskipun itu tidak mungkin di C ++ 03).
Mehrdad
5

C ++ adalah bahasa multi-paradigma, yang artinya ia mencoba mendukung banyak cara berpikir. Kadang-kadang fitur C ++ lebih canggung atau kurang lancar daripada implementasi bahasa lain, seperti halnya dengan pemrograman fungsional.

Yang mengatakan, saya tidak bisa memikirkan bagian atas kepala saya dari fitur bahasa C ++ asli yang melakukan apa yang dilakukan yielddengan Python atau JavaScript.

Contoh lain adalah pemrograman konkuren . C ++ 0x akan mengatakan tentang hal itu, tetapi standar saat ini tidak, dan konkurensi adalah cara berpikir yang sama sekali baru.

Juga, pengembangan yang cepat - bahkan pemrograman shell - adalah sesuatu yang tidak akan pernah Anda pelajari jika Anda tidak pernah meninggalkan domain pemrograman C ++.

wilhelmtell
sumber
Saya bahkan tidak bisa mulai berpikir betapa sulitnya membuat generator di C ++ mengingat C ++ 2003. C ++ 2011 akan membuatnya lebih mudah, tetapi masih non-sepele. Bekerja secara rutin dengan C ++, C # dan Python, generator dengan mudah adalah fitur yang paling saya lewatkan di C ++ (sekarang C ++ 2011 telah menambahkan lambdas).
Saya tahu saya akan tertembak untuk ini, tetapi jika saya benar-benar harus mengimplementasikan generator di C ++, saya harus menggunakan ... setjmpdan longjmp. Saya tidak tahu berapa banyak yang rusak, tapi saya kira pengecualian akan menjadi yang pertama. Sekarang, jika Anda permisi, saya perlu membaca kembali Modern C ++ Design untuk mengeluarkannya dari kepala saya.
Mike DeSimone
@Mike DeSimone, dapatkah Anda menguraikan (singkat) tentang bagaimana Anda akan mencoba solusi dengan setjmp dan longjmp?
1
Coroutine isomorfik untuk functors.
GManNickG
1
@Xeo, @Mike: xkcd.com/292
Mehrdad
5

Coroutine adalah fitur bahasa yang sangat berguna yang menopang banyak manfaat nyata dari bahasa lain selain C ++. Mereka pada dasarnya menyediakan tumpukan tambahan sehingga fungsi dapat terganggu dan dilanjutkan, menyediakan fasilitas seperti pipa ke bahasa yang dengan mudah memberi makan hasil operasi melalui filter ke operasi lain. Ini luar biasa, dan di Ruby saya menemukannya sangat intuitif dan elegan. Evaluasi malas terkait dengan ini juga.

Introspeksi dan kompilasi kode run-time / eksekusi / evaluasi / apa pun adalah fitur yang sangat kuat yang kurang C ++.

Tony
sumber
Coroutine tersedia di FreeRTOS ( lihat di sini ), yang diimplementasikan dalam C. Saya ingin tahu apa yang diperlukan untuk membuatnya bekerja di C ++?
Mike DeSimone
Co-rutin adalah hack jahat untuk meniru objek dalam C. Dalam C ++, objek digunakan untuk bundel kode dan data. Tetapi dalam C, Anda tidak bisa. Jadi Anda menggunakan tumpukan rutin untuk menyimpan data, dan fungsi rutin bersama untuk menyimpan kode.
MSalters
Coroutines di C ++: crystalclearsoftware.com/soc/coroutine
Ferruccio
1
@Ferruccio: Terima kasih atas tautannya ... ada beberapa di artikel Wikipedia juga. @MSalters: apa yang membuat Anda menggambarkan co-rutinitas sebagai "hack jahat"? Tampaknya perspektif yang sangat sewenang-wenang bagi saya. Menggunakan tumpukan untuk menyimpan keadaan juga dilakukan oleh algoritma rekursif - apakah mereka juga hackish? FWIW, coroutine, dan OOP datang ke lokasi sekitar waktu yang sama (awal 1960-an) ... untuk mengatakan pembajak itu adalah peretasan untuk objek di C tampak aneh ... Saya membayangkan beberapa programmer C saat itu tertarik untuk meniru objek, > 15 tahun sebelum C ++.
Tony
4

Setelah menerapkan sistem aljabar komputer dalam Lisp dan C ++, saya dapat memberi tahu Anda bahwa tugas itu jauh lebih mudah di Lisp, meskipun saya adalah pemula yang lengkap dalam bahasa ini. Sifat sederhana dari daftar semua ini menyederhanakan banyak algoritma. Memang, versi C ++ adalah zillions kali lebih cepat. Ya, saya bisa membuat versi lisp lebih cepat, tetapi kode tidak akan lispy. Scripting adalah hal lain yang selalu lebih mudah adalah lisp, misalnya. Ini semua tentang menggunakan alat yang tepat untuk pekerjaan itu.

jeffythedragonslayer
sumber
Apa perbedaan kecepatan?
quant_dev
1
@quant_dev: kelipatan satu miliar, tentu saja!
Matt Ellen
Saya tidak pernah benar-benar mengukurnya tetapi saya merasa bahwa O besar itu berbeda. Saya awalnya menulis versi C ++ dalam gaya fungsional dan memiliki masalah kecepatan juga sampai saya mengajarinya untuk memodifikasi struktur data alih-alih membuat yang baru, yang diubah. Tapi itu juga membuat kode lebih sulit dibaca ...
jeffythedragonslayer
2

Apa yang kita maksudkan ketika kita mengatakan bahwa satu bahasa "lebih kuat" daripada yang lain? Ketika kita mengatakan bahwa bahasa itu "ekspresif?" Atau "kaya?" Saya pikir kita maksudkan bahwa bahasa memperoleh kekuatan ketika bidang pandangnya cukup sempit untuk membuatnya mudah dan alami untuk menggambarkan masalah - benar-benar transisi negara, bukan? - yang hidup dalam pandangan itu. Namun bahasa itu sangat kurang kuat, kurang ekspresif, dan kurang bermanfaat ketika bidang pandang kita melebar.

Semakin "kuat" dan "ekspresif" bahasa, semakin terbatas penggunaannya. Jadi mungkin "kuat" dan "ekspresif" adalah kata-kata yang salah untuk digunakan untuk alat utilitas sempit. Mungkin "pantas" atau "abstrak" adalah kata-kata yang lebih baik untuk hal-hal seperti itu.

Saya mulai dalam pemrograman dengan menulis banyak hal tingkat rendah: driver perangkat dengan rutinitas interupsi mereka; program tertanam; kode sistem operasi. Kode itu intim dengan perangkat keras dan saya menulis semuanya dalam bahasa assembly. Kami tidak akan mengatakan assembler dalam bahasa yang paling abstrak, namun itu dan merupakan bahasa yang paling kuat dan ekspresif dari mereka semua. Saya dapat mengungkapkan masalah apa pun dalam bahasa assembly; itu sangat kuat sehingga saya bisa melakukan apa saja yang saya suka dengan mesin apa pun.

Dan semua pemahaman saya selanjutnya tentang bahasa tingkat yang lebih tinggi berhutang semuanya pada pengalaman saya dengan assembler. Semua yang saya pelajari kemudian mudah karena, Anda tahu, semuanya - tidak peduli seberapa abstrak - pada akhirnya harus menyesuaikan diri dengan perangkat keras.

Anda mungkin ingin melupakan tingkat abstraksi yang lebih tinggi dan lebih tinggi - yaitu bidang pandang yang semakin sempit. Anda selalu dapat mengambilnya nanti. Sangat mudah untuk belajar, dalam hitungan hari. Menurut pendapat saya, Anda akan lebih baik belajar bahasa perangkat keras 1 , untuk sedekat mungkin dengan tulang.


1 Mungkin tidak terlalu erat, tetapi cardan cdrmengambil nama mereka dari perangkat keras: Lisp pertama berjalan pada mesin yang memiliki Daftar Penurunan aktual dan Daftar Alamat aktual. Bagaimana dengan itu?

Pete Wilson
sumber
Anda menemukan bahwa pilihan adalah pedang bermata dua. Kita semua mencarinya, tetapi ada sisi gelapnya dan itu membuat kita tidak bahagia. Lebih baik memiliki pandangan yang sangat jelas tentang dunia dan batasan-batasan yang dapat Anda operasikan. Orang-orang adalah makhluk yang sangat kreatif, dan dapat melakukan hal-hal besar dengan alat yang terbatas. Jadi singkatnya, saya katakan itu bukan bahasa pemrograman tetapi memiliki orang-orang berbakat yang dapat membuat bahasa apa pun bernyanyi!
Chad
"Memberi saran berarti menciptakan; mendefinisikan adalah menghancurkan." Berpikir Anda bisa membuat hidup lebih mudah dengan bahasa lain terasa enak, tetapi begitu Anda melompat, maka Anda harus berurusan dengan kutil bahasa baru.
Mike DeSimone
2
Saya akan mengatakan bahwa bahasa Inggris jauh lebih kuat dan ekspresif daripada bahasa pemrograman mana pun, namun batas kegunaannya meluas setiap hari dan utilitasnya sangat besar. Bagian dari kekuatan dan ekspresif berasal dari kemampuan untuk berkomunikasi pada tingkat abstraksi yang sesuai dan kemampuan untuk menemukan tingkat abstraksi baru ketika terhubung.
molbdnilo
@ Mike maka Anda harus berurusan dengan berkomunikasi dengan bahasa sebelumnya dalam yang baru;)
Chad
2

Array Asosiatif

Cara khas memproses data adalah:

  • membaca input dan membangun struktur hirarkis darinya,
  • membuat indeks untuk struktur itu (mis. urutan berbeda),
  • membuat ekstrak (bagian yang disaring) dari mereka,
  • menemukan nilai atau sekelompok nilai (node),
  • atur ulang struktur (hapus node, tambahkan, tambahkan, hapus sub-elemen berdasarkan aturan, dll.),
  • memindai melalui pohon dan mencetak atau menyimpan beberapa bagiannya.

Alat yang tepat untuk itu adalah array asosiatif .

  • Dukungan bahasa terbaik untuk array asosiatif yang pernah saya lihat adalah MUMPS , di mana array asosiatif adalah: 1. selalu diurutkan 2. mereka dapat dibuat pada disk (disebut database), dengan sintaks yang sama. (Efek samping: ini sangat kuat sebagai basis data, programmer memiliki akses ke btree asli. Sistem NoSQL terbaik yang pernah ada.)
  • Hadiah kedua saya pergi ke PHP , saya suka foreach dan sintaks mudah, seperti $ a [] = x atau $ a [x] [y] [z] ++ .

Saya tidak terlalu menyukai sintaksis asosiatif array JavaScript, karena saya tidak dapat membuat, katakanlah [x] [y] [z] = 8 , pertama-tama saya harus membuat [x] dan [x] [y] .

Oke, di C ++ (dan di Jawa) ada portofolio kelas wadah yang bagus, Peta , Multimap , apa pun, tetapi jika saya ingin memindai, saya harus membuat iterator, dan ketika saya ingin memasukkan elemen level-dalam yang baru, saya harus membuat semua level atas dll. Tidak nyaman.

Saya tidak mengatakan bahwa tidak ada array asosiatif yang dapat digunakan di C ++ (dan Java), tetapi bahasa skrip yang tidak diketik (atau diketik tidak ketat) mengalahkan yang dikompilasi, karena mereka adalah bahasa skrip yang tidak bertipe.

Penafian: Saya tidak terbiasa dengan C # dan bahasa .NET lainnya, AFAIK mereka memiliki penanganan array asosiatif yang baik.

ern0
sumber
1
Pengungkapan penuh: MUMPS bukan untuk semua orang. Mengutip: Untuk memberikan sedikit lebih banyak "dunia nyata" contoh horor yaitu MUMPS, mulailah dengan mengambil satu bagian Kontes Kode C Internasional Sempit, sejumput Perl, dua langkah penumpukan FORTRAN dan SNOBOL, dan kontribusi independen dan tidak terkoordinasi dari puluhan peneliti medis, dan begitulah.
Mike DeSimone
1
Dalam Python, Anda bisa menggunakan dicttipe bawaan (mis. x = {0: 5, 1: "foo", None: 500e3}, Perhatikan bahwa tidak ada persyaratan untuk kunci atau nilai-nilai untuk jenis yang sama). Mencoba melakukan sesuatu seperti a[x][y][z] = 8itu sulit karena bahasa harus melihat ke masa depan untuk melihat apakah Anda akan menetapkan nilai atau membuat level lain; ekspresi a[x][y]itu sendiri tidak memberi tahu Anda.
Mike DeSimone
MUMPS pada awalnya adalah bahasa mirip-Dasar dengan array asosiatif (dapat langsung disimpan di disk!). Versi selanjutnya berisi ekstensi prosedural, yang membuatnya sangat mirip dengan PHP inti. Orang yang takut pada Dasar dan PHP akan menganggap Mumps menakutkan, tetapi yang lain tidak. Pemrogram tidak. Dan ingat, ini adalah sistem yang sangat lama, semua hal aneh, seperti instruksi satu huruf (walaupun, Anda dapat menggunakan nama lengkap), urutan evaluasi LR dll. - serta solusi yang tidak aneh - hanya memiliki satu tujuan: optimasi .
ern0
"Kita harus melupakan efisiensi kecil, katakanlah sekitar 97% dari waktu: optimasi prematur adalah akar dari semua kejahatan. Namun kita tidak boleh melewatkan peluang kita dalam 3% kritis itu." - Donald Knuth. Apa yang Anda gambarkan terdengar bagi saya seperti bahasa warisan yang penekanannya adalah kompatibilitas ke belakang, dan tidak apa-apa. Secara pribadi, dalam aplikasi itu, saya menganggap rawatan lebih penting daripada optimasi, dan bahasa dengan ekspresi non-aljabar dan perintah satu huruf terdengar kontraproduktif. Saya melayani pelanggan, bukan bahasa.
Mike DeSimone
@ Mike: tautan yang sangat menghibur yang Anda poskan, saya tertawa gembira saat membacanya.
antar
2

Saya belajar Java, C \ C ++, Assembly, dan Java Script. Saya menggunakan C ++ untuk mencari nafkah.

Meskipun, saya harus mengatakan saya lebih suka pemrograman Majelis dan pemrograman C lebih. Ini sebagian besar sejalan dengan pemrograman Imperatif.

Saya tahu bahwa pemrograman Paradigma penting untuk mengkategorikan tipe data, dan memberikan konsep abstrak pemrograman yang lebih tinggi untuk memungkinkan pola desain yang kuat dan formalisasi kode. Meskipun dalam arti tertentu, setiap Paradigma adalah kumpulan pola dan koleksi untuk abstrak lapisan perangkat keras yang mendasarinya sehingga Anda tidak perlu memikirkan EAX, atau IP secara internal di dalam mesin.

Satu-satunya masalah saya dengan ini, adalah memungkinkan gagasan dan konsep masyarakat tentang cara kerja mesin diubah menjadi pernyataan Ideologi dan ambigu tentang apa yang sedang terjadi. Roti ini semua jenis abstraksi yang indah di atas abstrak untuk beberapa tujuan ideologi programmer.

Pada akhirnya, lebih baik memiliki pola pikir dan batasan yang jelas tentang apa CPU itu dan bagaimana komputer bekerja di bawah tenda. Semua CPU peduli tentang mengeksekusi serangkaian instruksi yang memindahkan data masuk dan keluar dari memori ke dalam register dan melakukan instruksi. Tidak memiliki konsep tipe data, atau konsep pemrograman yang lebih tinggi. Itu hanya memindahkan data.

Itu menjadi lebih kompleks ketika Anda menambahkan paradigma pemrograman ke dalam campuran karena pandangan kita tentang dunia semuanya berbeda.

Chad
sumber
2

Adakah fitur atau idiom bahasa yang kuat yang Anda gunakan dalam bahasa yang akan sulit untuk dikonseptualisasikan atau diimplementasikan jika Anda hanya menulis dalam c ++?

Apakah ada konsep atau teknik yang berguna yang Anda temui dalam bahasa lain yang Anda rasa sulit untuk dikonsep jika Anda menulis atau "berpikir" dalam c ++?

C ++ membuat banyak pendekatan menjadi sulit. Saya akan mengatakan bahwa sebagian besar pemrograman sulit dikonsep jika Anda membatasi diri pada C ++. Berikut adalah beberapa contoh masalah yang lebih mudah dipecahkan dengan cara yang membuat C ++ sulit.

Registrasi alokasi dan konvensi panggilan

Banyak orang menganggap C ++ sebagai bahasa tingkat rendah bare metal tetapi sebenarnya tidak. Dengan mengabstraksikan detail-detail penting dari mesin, C ++ mempersulit untuk membuat konsep kepraktisan seperti alokasi register dan konvensi pemanggilan.

Untuk mempelajari tentang konsep-konsep seperti ini, saya sarankan untuk ikut program bahasa assembly dan lihat artikel ini tentang kualitas pembuatan kode ARM .

Pembuatan kode run-time

Jika Anda hanya tahu C ++ maka Anda mungkin berpikir bahwa templat adalah semua-dan-semua metaprogramming. Mereka bukan. Bahkan, mereka adalah alat yang buruk secara objektif untuk metaprogramming. Setiap program yang memanipulasi program lain adalah metaprogram, termasuk interpreter, kompiler, sistem aljabar komputer dan pembuktian teorema. Pembuatan kode run-time adalah fitur yang berguna untuk ini.

Saya merekomendasikan untuk memulai implementasi Skema dan bermain EVALuntuk belajar tentang evaluasi metacircular.

Memanipulasi pohon

Pohon ada di mana-mana dalam pemrograman. Dalam parsing Anda memiliki pohon sintaksis abstrak. Dalam kompiler Anda memiliki IR yang merupakan pohon. Dalam pemrograman grafis dan GUI Anda memiliki pohon adegan.

Ini "Ridiculously Simple JSON Parser untuk C ++" beratnya hanya 484 LOC yang sangat kecil untuk C ++. Sekarang bandingkan dengan parser JSON sederhana saya sendiri yang beratnya hanya 60 LOC dari F #. Perbedaannya terutama karena tipe data aljabar ML dan pencocokan pola (termasuk pola aktif) membuatnya jauh lebih mudah untuk memanipulasi pohon.

Lihat juga pohon merah-hitam di OCaml .

Struktur data murni fungsional

Kurangnya GC di C ++ membuatnya praktis tidak mungkin untuk mengadopsi beberapa pendekatan yang bermanfaat. Struktur data yang murni fungsional adalah salah satu alat tersebut.

Misalnya, lihat pencocokan ekspresi reguler 47-baris ini di OCaml. Singkatnya ini sebagian besar disebabkan oleh penggunaan luas struktur data yang murni fungsional. Khususnya, penggunaan kamus dengan kunci yang ditetapkan. Itu sangat sulit dilakukan di C ++ karena kamus dan set stdlib semuanya bisa berubah tetapi Anda tidak bisa mengubah kunci kamus atau Anda merusak koleksi.

Pemrograman logika dan batalkan buffer adalah contoh praktis lainnya di mana struktur data yang murni fungsional membuat sesuatu yang sulit di C ++ sangat mudah dalam bahasa lain.

Buntut panggilan

Tidak hanya C + + tidak menjamin panggilan ekor tetapi RAII secara fundamental bertentangan dengan itu karena destruktor menghalangi jalannya panggilan di posisi ekor. Tail tail memungkinkan Anda membuat jumlah panggilan fungsi yang tidak terbatas hanya menggunakan jumlah ruang stack yang terbatas. Ini bagus untuk mengimplementasikan mesin negara, termasuk mesin negara yang dapat diperpanjang dan itu adalah kartu "keluar dari penjara" yang bagus dalam banyak keadaan yang canggung.

Misalnya, lihat penerapan masalah 0-1 ransel menggunakan gaya kelanjutan-lewat dengan memoisasi dalam F # dari industri keuangan. Ketika Anda memiliki panggilan ekor, gaya meneruskan kelanjutan bisa menjadi solusi yang jelas tetapi C ++ membuatnya tidak bisa diterapkan.

Konkurensi

Contoh nyata lainnya adalah pemrograman konkuren. Meskipun ini sepenuhnya mungkin dalam C ++ itu sangat rawan kesalahan dibandingkan dengan alat lain, terutama berkomunikasi proses berurutan seperti yang terlihat dalam bahasa seperti Erlang, Scala dan F #.

Jon Harrop
sumber
1

Ini adalah pertanyaan lama, tetapi karena tidak ada yang menyebutkannya, saya akan menambahkan daftar (dan sekarang dict) pemahaman. Sangat mudah untuk menulis satu-liner di Haskell atau Python yang memecahkan masalah Fizz-Buzz. Coba lakukan itu di C ++.

Sementara C ++ membuat gerakan besar ke modernitas dengan C ++ 11, itu sedikit sulit untuk menyebutnya bahasa "modern". C ++ 17 (yang belum dirilis) sedang membuat lebih banyak langkah untuk mencapai standar modern, selama "modern" berarti "bukan dari milenium sebelumnya".

Bahkan pemahaman yang paling sederhana yang dapat dituliskan dalam satu baris dalam Python (dan mematuhi batas panjang garis 79 karakter Guido) menjadi banyak dan banyak baris kode ketika diterjemahkan ke C ++, dan beberapa baris kode C ++ tersebut agak berbelit-belit.

David Hammen
sumber
Catatan baik: Sebagian besar pemrograman saya dalam C ++. Saya suka bahasanya.
David Hammen
Saya pikir Proposal Ranges seharusnya menyelesaikan yang ini? (Bahkan di C ++ 17 saya pikir)
Martin Ba
2
"perpindahan besar ke modernitas": Fitur "modern" apa yang disediakan C ++ 11 yang ditemukan pada milenium saat ini?
Giorgio
@MartinBa - Pemahaman saya tentang proposal "rentang" adalah bahwa ini adalah pengganti untuk iterator yang lebih mudah untuk dikerjakan dan lebih sedikit rawan kesalahan. Saya belum melihat saran bahwa mereka akan membiarkan sesuatu yang semenarik daftar pemahaman.
Jules
2
@Giorgio - fitur apa dari bahasa populer apa pun yang ditemukan di milenium saat ini?
Jules
0

Pustaka yang dikompilasi memanggil panggilan balik, yang merupakan fungsi anggota yang ditentukan pengguna dari kelas yang ditentukan pengguna.


Ini dimungkinkan di Objective-C, dan itu membuat pemrograman antarmuka pengguna mudah. Anda dapat memberi tahu tombol: "Tolong, panggil metode ini untuk objek ini ketika Anda ditekan", dan tombol akan melakukannya. Anda bebas menggunakan nama metode apa pun untuk panggilan balik yang Anda sukai, itu tidak dibekukan dalam kode perpustakaan, Anda tidak harus mewarisi dari adaptor agar berfungsi, juga kompilator tidak ingin menyelesaikan panggilan pada waktu kompilasi, dan, sama pentingnya, Anda dapat memberi tahu dua tombol untuk memanggil dua metode berbeda dari objek yang sama.

Saya belum melihat cara yang fleksibel untuk mendefinisikan callback dalam bahasa lain (meskipun saya akan sangat tertarik untuk mendengar tentang mereka!). Setara terdekat di C ++ mungkin melewati fungsi lambda yang melakukan panggilan yang diperlukan, yang lagi-lagi membatasi kode perpustakaan menjadi templat.

Fitur Objective-C inilah yang telah mengajarkan saya untuk menghargai kemampuan suatu bahasa untuk melewatkan semua jenis objek / fungsi / apa pun-konsep-penting-bahasa-yang terkandung di sekitar secara bebas, bersama dengan kekuatan untuk menyimpannya ke variabel. Setiap titik dalam bahasa yang mendefinisikan semua jenis konsep, tetapi tidak menyediakan sarana untuk menyimpannya (atau referensi untuk itu) di semua jenis variabel yang tersedia, adalah batu sandungan yang signifikan, dan kemungkinan sumber yang jauh lebih buruk, kode duplikat. Sayangnya, bahasa pemrograman barok cenderung menunjukkan sejumlah poin ini:

  • Dalam C ++ Anda tidak bisa menuliskan jenis VLA, atau menyimpan pointer ke sana. Ini secara efektif melarang array multidimensi sebenarnya dari ukuran dinamis (yang tersedia di C sejak C99).

  • Dalam C ++ Anda tidak dapat menuliskan jenis lambda. Anda bahkan tidak bisa mengetikkannya. Dengan demikian, tidak ada cara untuk menyebarkan lambda, atau menyimpan referensi untuk suatu objek. Fungsi Lambda hanya dapat diteruskan ke templat.

  • Di Fortran Anda tidak dapat menuliskan jenis daftar nama. Tidak ada cara untuk mengirimkan daftar nama ke segala jenis rutinitas. Jadi, jika Anda memiliki algoritme kompleks yang harus dapat menangani dua daftar nama yang berbeda, Anda kurang beruntung. Anda tidak bisa hanya menulis algoritme satu kali dan meneruskan daftar nama yang relevan kepadanya.

Ini hanya beberapa contoh, tetapi Anda melihat titik yang sama: Setiap kali Anda melihat pembatasan seperti itu untuk pertama kalinya, Anda biasanya tidak akan peduli karena tampaknya ide gila untuk melakukan hal terlarang. Namun, ketika Anda melakukan pemrograman yang sungguh-sungguh dalam bahasa itu, Anda akhirnya sampai pada titik di mana pembatasan yang tepat ini menjadi gangguan nyata.

cmaster
sumber
1
I have not seen a similarly flexible way to define a callback in any other language yet (though I'd be very interested to hear about them!) Apa yang baru saja Anda deskripsikan terdengar persis seperti cara kerja kode UI berbasis-acara di Delphi. (Dan dalam. WinForms NET, yang sangat dipengaruhi oleh Delphi.)
Mason Wheeler
2
"Dalam C ++ Anda tidak dapat menuliskan jenis VLA" [...] - dalam C ++, VLA gaya C99 tidak diperlukan, karena kami memilikinya std::vector. Meskipun sedikit kurang efisien karena tidak menggunakan alokasi tumpukan, secara fungsional isomorfik untuk VLA, jadi tidak benar-benar dianggap sebagai masalah tipe "blub": Pemrogram C ++ dapat melihat cara kerjanya dan hanya berkata, "ah ya , C melakukan itu lebih efisien daripada C ++ ".
Jules
2
"Dalam C ++ Anda tidak bisa menuliskan jenis lambda. Anda bahkan tidak bisa mengetikkannya. Dengan demikian, tidak ada cara untuk menyebarkan lambda, atau menyimpan referensi untuknya di objek" - itulah gunanya std::function.
Jules
3
"Saya belum melihat cara yang sama fleksibelnya untuk mendefinisikan panggilan balik dalam bahasa lain apa pun (meskipun saya akan sangat tertarik mendengarnya!)." - di Jawa, Anda dapat menulis object::methoddan itu akan diubah menjadi sebuah instance antarmuka apa pun yang diharapkan oleh kode penerima. C # memiliki delegasi. Setiap bahasa objek-fungsional memiliki fitur ini karena pada dasarnya titik penampang kedua paradigma.
Jules
@ Jules Argumen Anda persis seperti apa Blub-Paradox: Sebagai seorang programmer C ++ yang mahir, Anda tidak melihat ini sebagai batasan. Namun, mereka adalah batasan, dan bahasa lain seperti C99 lebih kuat dalam hal-hal khusus ini. Ke poin terakhir Anda: Ada beberapa solusi yang mungkin dalam banyak bahasa, tetapi saya tidak tahu satu yang benar-benar memungkinkan Anda untuk memberikan nama metode apa pun ke beberapa kelas lain dan meminta untuk menyebutnya pada beberapa objek yang Anda berikan juga.
cmaster