Bahkan bahasa di mana Anda memiliki manipulasi pointer eksplisit seperti C selalu dilewati oleh nilai (Anda dapat meneruskannya dengan referensi tetapi itu bukan perilaku default).
Apa manfaatnya ini, mengapa begitu banyak bahasa yang dilewatkan oleh nilai-nilai dan mengapa yang lain lulus dengan referensi ? (Saya mengerti Haskell disahkan oleh referensi meskipun saya tidak yakin).
programming-languages
language-design
Kode saya tidak memiliki bug
sumber
sumber
void acceptEntireProgrammingLanguageByValue(C++);
temp := a; a := b; b := temp;
Dan ketika kembali, nilaia
danb
akan ditukar. Tidak ada cara untuk melakukan itu di C; Anda harus menyampaikan petunjuka
danb
danswap
rutinitas harus bertindak berdasarkan nilai yang mereka tunjuk.Jawaban:
Pass by value seringkali lebih aman daripada pass by reference, karena Anda tidak dapat secara tidak sengaja memodifikasi parameter ke metode / fungsi Anda. Ini membuat bahasa lebih mudah digunakan, karena Anda tidak perlu khawatir tentang variabel yang Anda berikan ke suatu fungsi. Anda tahu mereka tidak akan diubah, dan ini yang sering Anda harapkan .
Namun, jika Anda ingin memodifikasi parameter, Anda harus memiliki beberapa operasi eksplisit untuk memperjelas hal ini (menyampaikan pointer). Ini akan memaksa semua penelepon Anda untuk membuat panggilan sedikit berbeda (
&variable
, dalam C) dan ini membuatnya eksplisit bahwa parameter variabel dapat diubah.Jadi sekarang Anda dapat mengasumsikan bahwa suatu fungsi tidak akan mengubah parameter variabel Anda, kecuali jika itu ditandai secara eksplisit untuk melakukannya (dengan mengharuskan Anda untuk memasukkan pointer). Ini adalah solusi yang lebih aman dan bersih daripada alternatifnya: Asumsikan semuanya dapat mengubah parameter Anda, kecuali mereka secara khusus mengatakan mereka tidak bisa.
sumber
Call-by-value dan call-by-reference adalah teknik implementasi yang keliru untuk mode parameter-passing sejak lama.
Awalnya, ada FORTRAN. FORTRAN hanya memiliki call-by-reference, karena subrutin harus dapat memodifikasi parameter mereka, dan siklus komputasi terlalu mahal untuk memungkinkan beberapa mode parameter-passing, ditambah tidak cukup diketahui tentang pemrograman ketika FORTRAN pertama kali didefinisikan.
ALGOL datang dengan nama panggilan dan nilai panggilan. Nilai panggilan-adalah untuk hal-hal yang tidak seharusnya diubah (parameter input). Call-by-name adalah untuk parameter output. Panggilan-dengan-nama ternyata menjadi tempayan besar, dan ALGOL 68 menjatuhkannya.
PASCAL menyediakan call-by-value dan call-by-reference. Itu tidak menyediakan cara bagi programmer untuk memberitahu kompiler bahwa ia melewati objek besar (biasanya array) dengan referensi, untuk menghindari meniup tumpukan parameter, tetapi objek tidak boleh diubah.
PASCAL menambahkan pointer ke leksikon desain bahasa.
C memberikan call-by-value, dan simulasi call-by-reference dengan mendefinisikan operator kludge untuk mengembalikan pointer ke objek yang berubah-ubah dalam memori.
Kemudian bahasa-bahasa disalin C, terutama karena para perancang tidak pernah melihat yang lain. Ini mungkin mengapa panggilan-oleh-nilai sangat populer.
C ++ menambahkan kludge di atas kludge C untuk memberikan panggilan-oleh-referensi.
Sekarang, sebagai akibat langsung dari call-by-value vs call-by-reference vs call-by-pointer-kludge, C dan C ++ (programmer) mengalami sakit kepala yang mengerikan dengan pointer pointer dan pointer ke const (read-only) benda.
Ada yang berhasil menghindari seluruh mimpi buruk ini.
Ada tidak memiliki call-by-value secara eksplisit vs call-by-reference. Alih-alih, Ada memiliki parameter (yang dapat dibaca tetapi tidak ditulis), parameter keluar (yang HARUS ditulis sebelum dapat dibaca), dan parameter keluar, yang dapat dibaca dan ditulis dalam urutan apa pun. Compiler memutuskan apakah parameter tertentu dilewatkan oleh nilai atau dengan referensi: itu transparan untuk programmer.
sumber
In the beginning, there was FORTRAN
.0
awalan tidak konsisten dengan setiap awalan sepuluh non-basis lainnya. Itu mungkin agak dimaafkan dalam C (dan sebagian besar bahasa yang kompatibel dengan sumber, misalnya C ++, Objective C), jika oktal adalah satu - satunya basis alternatif ketika diperkenalkan, tetapi ketika bahasa yang lebih modern menggunakan keduanya0x
dan0
dari awal, itu hanya membuat mereka terlihat buruk dipikirkan.Lulus dengan referensi memungkinkan efek samping yang sangat tidak disengaja yang sangat sulit untuk mustahil dilacak ketika mereka mulai menyebabkan perilaku yang tidak diinginkan.
Pass by value, terutama
final
,static
atauconst
parameter input membuat seluruh kelas bug ini menghilang.Bahasa yang tidak dapat berubah bahkan lebih deterministik dan lebih mudah untuk berpikir tentang dan memahami apa yang sedang terjadi dan apa yang diharapkan keluar dari suatu fungsi.
sumber
Swap
peretasan yang tidak menggunakan variabel sementara, meskipun tidak cenderung menjadi masalah sendiri, menunjukkan betapa sulitnya contoh yang lebih rumit untuk melakukan debug.Inti dari memecah program besar menjadi subrutin kecil adalah Anda dapat menggunakan subrutin secara mandiri. Referensi per langkah merusak properti ini. (Seperti halnya keadaan yang dapat diubah yang dibagikan.)
Sebenarnya, C selalu pass-by-value, tidak pernah pass-by-reference. Anda dapat mengambil alamat sesuatu dan meneruskan alamat itu, tetapi alamat tersebut tetap akan diteruskan berdasarkan nilai.
Ada dua alasan utama untuk menggunakan pass-by-reference:
Secara pribadi, saya pikir # 1 adalah palsu: hampir selalu menjadi alasan untuk API buruk dan / atau desain bahasa:
Anda bisa juga mensimulasikan beberapa nilai pengembalian dengan mengemasnya menjadi beberapa struktur data ringan seperti tuple. Ini bekerja dengan baik jika bahasa mendukung pencocokan pola atau pengikatan ikatan. Misalnya Ruby:
Seringkali, Anda bahkan tidak memerlukan banyak nilai pengembalian. Misalnya, satu pola populer adalah bahwa subrutin mengembalikan kode kesalahan dan hasil aktual ditulis kembali dengan referensi. Sebagai gantinya, Anda hanya perlu melempar pengecualian jika kesalahannya tidak terduga atau mengembalikan
Either<Exception, T>
jika kesalahan tersebut diharapkan. Pola lain adalah mengembalikan boolean yang memberi tahu apakah operasi berhasil dan mengembalikan hasil aktual dengan referensi. Sekali lagi, jika kegagalannya tidak terduga, Anda harus melempar pengecualian, jika kegagalan diharapkan, misalnya ketika mencari nilai dalam kamus, Anda harus mengembalikannyaMaybe<T>
.Pass-by-reference mungkin lebih efisien daripada pass-by-value, karena Anda tidak perlu menyalin nilainya.
Tidak, Haskell bukan pass-by-reference. Juga bukan nilai per nilai. Pass-by-reference dan pass-by-value adalah strategi evaluasi yang ketat, tetapi Haskell tidak-ketat.
Bahkan, Haskell Keterangan tidak menentukan apa strategi evaluasi tertentu. Sebagian besar implementasi Hakell menggunakan campuran panggilan-dengan-nama dan panggilan-dengan-kebutuhan (varian panggilan-dengan-nama dengan memoisasi), tetapi standar tidak mengamanatkan ini.
Perhatikan bahwa bahkan untuk bahasa yang ketat, tidak masuk akal untuk membedakan antara pass-by-reference atau pass-by-value untuk bahasa fungsional karena perbedaan antara mereka hanya dapat diamati jika Anda bermutasi referensi. Oleh karena itu, implementor bebas memilih di antara keduanya, tanpa melanggar semantik bahasa.
sumber
Ada perilaku yang berbeda tergantung pada model panggilan bahasa, jenis argumen dan model memori bahasa.
Dengan tipe asli sederhana, lewat nilai memungkinkan Anda untuk melewati nilai oleh register. Itu bisa sangat cepat karena nilainya tidak perlu dimuat dari memori juga tidak menyimpan kembali. Optimalisasi serupa juga dimungkinkan hanya dengan menggunakan kembali memori yang digunakan oleh argumen begitu callee selesai dengan itu, tanpa risiko mengacaukan salinan penelepon objek. Jika argumennya adalah objek sementara, Anda mungkin akan menyimpan salinan lengkap melakukannya (C ++ 11 membuat jenis optimasi ini lebih jelas dengan referensi-kanan baru dan semantik langkahnya).
Dalam banyak bahasa OO (C ++ lebih merupakan pengecualian dalam konteks ini), Anda tidak bisa melewati objek dengan nilai. Anda dipaksa untuk melewatinya dengan referensi. Ini membuat kode polimorfik secara default dan lebih sejalan dengan gagasan instance yang tepat untuk OO. Juga, jika Anda ingin memberikan nilai, Anda harus membuat salinan sendiri, mengakui biaya kinerja yang menghasilkan tindakan tersebut. Dalam hal ini, bahasa yang dipilih untuk Anda adalah pendekatan yang lebih mungkin memberi Anda kinerja terbaik.
Dalam hal bahasa fungsional, saya kira memberikan nilai atau referensi hanyalah masalah optimasi. Karena fungsi dalam bahasa seperti itu murni dan bebas dari efek samping, sebenarnya tidak ada alasan untuk menyalin nilai kecuali untuk kecepatan. Saya bahkan cukup yakin bahwa bahasa seperti itu sering berbagi salinan objek yang sama dengan nilai yang sama, kemungkinan hanya tersedia jika Anda menggunakan semantic referensi lewat (konst). Python juga menggunakan trik ini untuk string integer dan umum (seperti metode dan nama kelas), menjelaskan mengapa integer dan string adalah objek konstan dalam Python. Ini juga membantu untuk mengoptimalkan kode lagi, dengan memungkinkan misalnya perbandingan pointer, bukan perbandingan konten, dan melakukan evaluasi malas beberapa data internal.
sumber
Anda dapat memberikan ekspresi berdasarkan nilai, itu wajar. Melewati ekspresi dengan referensi (sementara) adalah ... aneh.
sumber
Jika Anda melewati referensi, maka Anda secara efektif selalu bekerja dengan nilai-nilai global, dengan semua masalah global (yaitu cakupan dan efek samping yang tidak diinginkan).
Referensi, seperti halnya global, terkadang bermanfaat, tetapi seharusnya tidak menjadi pilihan pertama Anda.
sumber