Apakah ada alasan untuk tidak mengubah nilai parameter yang diteruskan oleh nilai?

9

Adakah argumen rekayasa-perangkat lunak yang obyektif dan dapat didukung untuk atau tidak memodifikasi nilai-nilai parameter nilai-dalam tubuh suatu fungsi?

Ludah berulang (sebagian besar menyenangkan) di tim saya adalah apakah parameter yang dilewati oleh nilai harus dimodifikasi atau tidak. Beberapa anggota tim bersikeras bahwa parameter tidak boleh ditugaskan, sehingga nilai yang semula diberikan ke fungsi selalu dapat diinterogasi. Saya tidak setuju dan berpendapat bahwa parameter tidak lebih dari variabel lokal yang diinisialisasi oleh sintaks memanggil metode; jika nilai asli dari parameter menurut nilai penting dari variabel lokal dapat dinyatakan untuk secara eksplisit menyimpan nilai ini. Saya tidak yakin bahwa kami berdua memiliki dukungan yang sangat baik untuk posisi kami.

Apakah ini konflik agama yang tak terselesaikan, atau adakah alasan rekayasa perangkat lunak objektif yang baik di kedua arah?

Catatan: Pertanyaan prinsip tetap terlepas dari detail implementasi bahasa tertentu. Dalam JavaScript, misalnya, di mana daftar argumen selalu dinamis, parameter dapat dianggap sebagai gula sintaksis untuk inisialisasi variabel lokal dari argumentsobjek. Meski begitu, orang dapat memperlakukan pengidentifikasi yang dinyatakan parameter sebagai "khusus" karena mereka masih menangkap berlalunya informasi dari penelepon ke callee.

Joshua Honig
sumber
Masih spesifik bahasa; berpendapat bahwa itu adalah agnostik bahasa karena "memang demikian, dari sudut pandang saya (bahasa favorit)" adalah bentuk argumen yang tidak valid. Alasan kedua mengapa itu khusus bahasa adalah karena pertanyaan yang tidak memiliki jawaban universal untuk semua bahasa tunduk pada budaya dan norma masing-masing bahasa; dalam hal ini, norma spesifik bahasa yang berbeda menghasilkan jawaban yang berbeda untuk pertanyaan ini.
rwong
Prinsip yang coba diajarkan oleh anggota tim Anda adalah "satu variabel, satu tujuan." Sayangnya, Anda juga tidak akan menemukan bukti definitif. Untuk apa nilainya, saya tidak ingat pernah mengkooptasi variabel parameter untuk tujuan lain, saya juga tidak ingat pernah mempertimbangkannya. Tidak pernah terpikir oleh saya bahwa itu mungkin ide yang bagus, dan saya masih berpikir itu tidak baik.
Robert Harvey
Saya pikir ini pasti sudah ditanyakan sebelumnya, tetapi satu-satunya penipuan yang saya temukan adalah pertanyaan SO yang lebih tua ini .
Doc Brown
3
Ini dianggap kurang rawan kesalahan untuk bahasa untuk melarang mutasi argumen, seperti halnya desainer bahasa fungsional menganggap "membiarkan" penugasan menjadi lebih sedikit rawan kesalahan. Saya ragu ada orang yang membuktikan asumsi ini menggunakan penelitian.
Frank Hileman

Jawaban:

15

Saya tidak setuju dan berpendapat bahwa parameter tidak lebih dari variabel lokal yang diinisialisasi oleh sintaks memanggil metode

Saya mengadopsi posisi ketiga: parameter sama seperti variabel lokal: keduanya harus diperlakukan tidak berubah secara default. Jadi variabel ditugaskan satu kali dan kemudian hanya membaca dari, tidak diubah. Dalam kasus loop, for each (x in ...)variabel tidak dapat diubah dalam konteks setiap iterasi. Alasan untuk ini adalah:

  1. itu membuat kode lebih mudah untuk "dieksekusi di kepalaku",
  2. itu memungkinkan lebih banyak nama deskriptif untuk variabel.

Dihadapkan dengan metode dengan sekelompok variabel yang ditugaskan dan kemudian tidak berubah, saya dapat fokus membaca kode, daripada mencoba mengingat nilai saat ini dari masing-masing variabel tersebut.

Dihadapkan dengan metode yang sama, kali ini dengan lebih sedikit variabel, tetapi itu mengubah nilai sepanjang waktu, saya sekarang harus mengingat keadaan saat ini dari variabel-variabel tersebut, serta bekerja pada apa yang dilakukan kode.

Dalam pengalaman saya, yang pertama jauh lebih mudah di otak saya yang buruk. Lainnya, lebih pintar, orang lain daripada saya mungkin tidak memiliki masalah ini, tetapi itu membantu saya.

Adapun poin lainnya:

double Foo(double r)
{
    r = r * r;
    r = Math.Pi * r;
    return r;
}

melawan

double Foo(int r)
{
    var rSquared = r * r;
    var area = Math.Pi * rSquared;
    return area;
} 

Contoh-contoh yang dibuat, tetapi bagi saya jauh lebih jelas mengenai apa yang terjadi dalam contoh kedua karena nama variabel: mereka memberikan informasi yang jauh lebih banyak kepada pembaca daripada massa yang rmelakukan dalam contoh pertama.

David Arno
sumber
"variabel lokal .... harus diperlakukan sebagai tidak dapat diubah secara default." - Apa?
whatsisname
1
Sangat sulit untuk menjalankan forloop dengan variabel yang tidak dapat diubah. Meringkas variabel dalam fungsi tidak cukup untuk Anda? Jika Anda mengalami kesulitan mengikuti variabel Anda dalam fungsi belaka, mungkin fungsi Anda terlalu panjang.
Robert Harvey
@RobertHarvey for (const auto thing : things)adalah untuk loop, dan variabel yang diperkenalkannya adalah (secara lokal) tidak berubah. for i, thing in enumerate(things):juga tidak bermutasi setiap penduduk setempat
Caleth
3
Ini adalah IMHO secara umum model mental yang sangat baik. Namun, satu hal yang ingin saya tambahkan: sering diterima untuk menginisialisasi variabel dalam lebih dari satu langkah dan memperlakukannya sebagai tidak dapat diubah sesudahnya. Ini tidak bertentangan dengan strategi umum yang disajikan di sini, hanya menentang mengikuti ini selalu secara harfiah.
Doc Brown
3
Wow, membaca komentar ini, saya bisa melihat bahwa banyak orang di sini jelas tidak pernah mempelajari paradigma fungsional.
Joshua Jones
4

Apakah ini konflik agama yang tak terselesaikan, atau adakah alasan rekayasa perangkat lunak objektif yang baik di kedua arah?

Itu satu hal yang saya sangat suka tentang (ditulis dengan baik) C ++: Anda dapat melihat apa yang diharapkan. const string& x1adalah referensi konstan, string x2adalah salinan dan const string x3salinan konstan. Saya tahu apa yang akan terjadi dalam fungsi tersebut. x2 akan diubah, karena jika tidak, tidak akan ada alasan untuk membuatnya non-const.

Jadi jika Anda memiliki bahasa yang memungkinkannya , nyatakan apa yang akan Anda lakukan dengan parameternya. Itu harus menjadi pilihan terbaik bagi semua orang yang terlibat.

Jika bahasa Anda tidak memungkinkan ini, saya khawatir tidak ada solusi peluru perak. Keduanya mungkin. Satu-satunya pedoman adalah prinsip kejutan paling tidak. Ambil satu pendekatan dan lakukan dengan itu di seluruh basis kode Anda, jangan campur.

tidak ada
sumber
Hal baik lainnya adalah itu int f(const T x)dan int f(T x)hanya berbeda dalam fungsi-tubuh.
Deduplicator
3
Fungsi C ++ dengan parameter seperti const int xhanya untuk membuat jelas x tidak diubah di dalamnya? Sepertinya gaya yang sangat aneh bagi saya.
Doc Brown
@DocBrown Saya tidak menilai salah satu gaya, saya hanya mengatakan bahwa seseorang yang berpikir parameter tidak boleh diubah dapat membuat kompiler menjaminnya dan seseorang yang berpikir mengubah mereka baik-baik saja dapat melakukannya juga. Kedua niat dapat dikomunikasikan dengan jelas melalui bahasa itu sendiri dan itu merupakan keuntungan besar dibandingkan bahasa yang tidak dapat menjaminnya dengan cara apa pun dan di mana Anda harus bergantung pada pedoman sewenang-wenang dan berharap semua orang membacanya dan menyesuaikannya.
novigt
@nvoigt: jika seseorang lebih memilih untuk memodifikasi parameter fungsi ("oleh-nilai"), ia hanya akan menghapus yang constjauh dari daftar parameter jika itu menghambatnya. Jadi saya tidak berpikir ini menjawab pertanyaan.
Doc Brown
@DocBrown Maka tidak lagi const. Tidak penting apakah itu const atau tidak, yang penting adalah bahasanya termasuk cara untuk mengkomunikasikannya kepada pengembang tanpa keraguan. Jawaban saya adalah tidak masalah, selama Anda berkomunikasi dengan jelas , yang membuat C ++ mudah, orang lain mungkin tidak dan Anda perlu konvensi.
novigt
1

Ya, itu adalah "konflik agama", kecuali bahwa Tuhan tidak membaca program (sejauh yang saya tahu), jadi itu diturunkan menjadi konflik keterbacaan yang menjadi masalah penilaian pribadi. Dan saya tentu saja telah melakukannya, dan melihatnya berhasil, keduanya. Sebagai contoh,

void propagate (OBJECT * objek, t0 ganda, dt ganda) {
  double t = t0; / * salinan lokal t0 untuk menghindari perubahan nilainya * /
  sambil (terserah) {
    timestep_the_object (...);
    t + = dt; }
  kembali; }

Jadi di sini, hanya karena nama argumen t0 , saya cenderung memilih untuk tidak mengubah nilainya. Tapi anggap saja, kita hanya mengubah nama argumen itu menjadi tNow , atau sesuatu seperti itu. Lalu saya lebih mungkin lupa tentang salinan lokal dan hanya menulis tNow + = dt; untuk pernyataan terakhir itu.

Tetapi sebaliknya, seandainya saya tahu bahwa klien yang menulis program ini akan mempekerjakan beberapa programmer baru dengan pengalaman yang sangat sedikit, yang akan membaca dan mengerjakan kode itu. Maka saya mungkin khawatir tentang membingungkan mereka dengan pass-by-value versus-referensi, sementara pikiran mereka 100% sibuk mencoba untuk mencerna semua logika bisnis. Jadi dalam kasus seperti ini saya selalu mendeklarasikan salinan lokal dari setiap argumen yang akan diubah, terlepas dari apakah itu lebih atau kurang dapat dibaca oleh saya.

Jawaban untuk pertanyaan gaya semacam ini muncul dengan pengalaman, dan jarang memiliki jawaban agama kanonik. Siapa pun yang mengira ada satu-dan-hanya-satu jawaban (dan bahwa ia mengetahuinya) mungkin perlu lebih banyak pengalaman.

John Forkosh
sumber