Apakah gaya yang baik untuk kembali secara eksplisit di Ruby?


155

Berasal dari latar belakang Python, di mana selalu ada "cara yang tepat untuk melakukannya" (cara "Pythonic") ketika datang ke gaya, saya bertanya-tanya apakah hal yang sama ada untuk Ruby. Saya telah menggunakan pedoman gaya saya sendiri tetapi saya berpikir untuk merilis kode sumber saya, dan saya ingin mematuhi aturan tidak tertulis yang mungkin ada.

Apakah itu "The Ruby Way" untuk secara eksplisit mengetikkan returnmetode? Saya sudah melihatnya selesai dengan dan tanpa, tetapi apakah ada cara yang tepat untuk melakukannya? Apakah mungkin ada waktu yang tepat untuk melakukannya? Sebagai contoh:

def some_func(arg1, arg2, etc)
  # Do some stuff...
  return value # <-- Is the 'return' needed here?
end

3
Ini adalah pertanyaan yang sangat lama, tetapi bagi mereka yang memiliki pertanyaan serupa, Buku Eloquent Ruby sangat disarankan. Ini bukan panduan gaya sebanyak pengantar untuk menulis Ruby idiomatik. Saya berharap lebih banyak orang akan membacanya!
Brian Dear

Sebagai seseorang yang mewarisi aplikasi warisan besar yang dikembangkan di Ruby, saya sangat menghargai Anda mengajukan pertanyaan ini. Ini layak mendapat dukungan. Ini berserakan di seluruh aplikasi warisan kami. Salah satu pekerjaan saya di tim ini adalah meningkatkan basis kode (sulit ketika baru di Ruby).
Stevers

Jawaban:


226

Pertanyaan lama (dan "dijawab"), tetapi saya akan melemparkan dua sen saya sebagai jawaban.

TL; DR - Anda tidak harus melakukannya, tetapi itu bisa membuat kode Anda jauh lebih jelas dalam beberapa kasus.

Meskipun tidak menggunakan pengembalian eksplisit mungkin "cara Ruby", itu membingungkan bagi programmer yang bekerja dengan kode asing, atau tidak terbiasa dengan fitur Ruby ini.

Ini adalah contoh yang agak dibuat-buat, tetapi bayangkan memiliki fungsi kecil seperti ini, yang menambahkan satu ke angka yang lewat, dan menugaskannya ke variabel instan.

def plus_one_to_y(x)
    @y = x + 1
end

Apakah ini dimaksudkan sebagai fungsi yang mengembalikan nilai, atau tidak? Sangat sulit untuk mengatakan apa yang dimaksud pengembang, karena keduanya memberikan variabel instan, DAN mengembalikan nilai yang ditetapkan juga.

Misalkan nanti, programmer lain (mungkin tidak terlalu familiar dengan bagaimana Ruby mengembalikan berdasarkan baris terakhir dari kode yang dieksekusi) datang dan ingin memasukkan beberapa pernyataan cetak untuk logging, dan fungsinya menjadi seperti ini ...

def plus_one_to_y(x)
    @y = x + 1
    puts "In plus_one_to_y"
end

Sekarang fungsinya rusak jika ada yang mengharapkan nilai yang dikembalikan . Jika tidak ada yang mengharapkan nilai yang dikembalikan, tidak apa-apa. Jelas jika suatu tempat lebih jauh ke bawah rantai kode, sesuatu yang memanggil ini mengharapkan nilai yang dikembalikan, itu akan gagal karena tidak mendapatkan kembali apa yang diharapkan.

Pertanyaan sebenarnya sekarang adalah ini: apakah ada yang benar-benar mengharapkan nilai yang dikembalikan? Apakah ini merusak sesuatu atau tidak? Akankah itu merusak sesuatu di masa depan? Siapa tahu! Hanya tinjauan kode lengkap dari semua panggilan yang akan memberi tahu Anda.

Jadi bagi saya setidaknya, pendekatan praktik terbaik adalah dengan sangat eksplisit bahwa Anda mengembalikan sesuatu jika itu penting, atau tidak menghasilkan apa-apa sama sekali ketika tidak.

Jadi dalam kasus fungsi demo kecil kami, dengan asumsi kami ingin mengembalikan nilai, itu akan ditulis seperti ini ...

def plus_one_to_y(x)
    @y = x + 1
    puts "In plus_one_to_y"
    return @y
end

Dan akan sangat jelas bagi setiap programmer bahwa ia mengembalikan nilai, dan jauh lebih sulit bagi mereka untuk memecahkannya tanpa menyadarinya.

Atau, orang dapat menulis seperti ini dan meninggalkan pernyataan kembali ...

def plus_one_to_y(x)
    @y = x + 1
    puts "In plus_one_to_y"
    @y
end

Tapi mengapa repot-repot meninggalkan kata kembali? Mengapa tidak letakkan saja di sana dan buatlah 100% jelas apa yang terjadi? Ini benar-benar tidak akan berdampak pada kemampuan kode Anda untuk melakukan.


6
Poin yang menarik. Saya pikir saya benar-benar mengalami situasi yang sama ketika saya mengajukan pertanyaan ini, di mana pengembang asli menggunakan nilai balik implisit dalam kode mereka, dan sangat sulit untuk memahami apa yang sedang terjadi. Terima kasih atas jawaban anda!
Sasha Chedygov

6
Saya menemukan pertanyaan ketika Googling tentang pengembalian implisit, karena saya baru saja dibakar oleh ini. Saya telah menambahkan beberapa logika pada akhir fungsi yang secara implisit mengembalikan sesuatu. Sebagian besar panggilan untuk itu tidak peduli (atau memeriksa) nilai yang dikembalikan, tetapi yang satu - dan gagal. Untungnya setidaknya itu muncul dalam beberapa tes fungsional.
Tim Holt

16
Meskipun benar, penting agar kode dapat dibaca, menghindari fitur yang terkenal dari bahasa pemrograman yang mendukung verbositas, menurut pendapat saya, praktik yang buruk. Jika pengembang tidak terbiasa dengan Ruby, maka mungkin mereka harus terbiasa sebelum menyentuh kode. Saya merasa agak konyol harus meningkatkan kekacauan kode saya hanya untuk acara di masa depan bahwa beberapa pengembang non-Ruby harus melakukan sesuatu nanti. Kita seharusnya tidak mengurangi bahasa ke penyebut umum terendah. Bagian dari keindahan Ruby adalah bahwa kita tidak harus menggunakan 5 baris kode untuk mengatakan sesuatu yang hanya perlu 3.
Brian

25
Jika kata itu returnmenambahkan makna semantik pada kode, mengapa tidak? Ini hampir tidak terlalu jelas - kita tidak berbicara 5 baris vs 3, hanya satu kata lagi. Saya lebih suka melihat returnsetidaknya dalam fungsi (mungkin tidak dalam blok) untuk mengekspresikan apa kode sebenarnya. Terkadang Anda harus mengembalikan fungsi-tengah - jangan tinggalkan returnkata kunci terakhir di baris terakhir hanya karena Anda bisa. Saya tidak menyukai verbositas sama sekali, tetapi saya mendukung konsistensi.
mindplay.dk

6
Ini jawaban yang bagus. Tetapi masih ada risiko bahwa Anda menulis metode Ruby yang berniat menjadi sebuah prosedur (alias unit pengembalian fungsi) misalnya side_effect()dan pengembang lain memutuskan untuk (ab) menggunakan nilai balik implisit dari prosedur Anda. Untuk memperbaiki ini, Anda dapat membuat prosedur Anda secara eksplisit return nil.
Alex Dean

66

Tidak. Gaya Ruby yang baik umumnya hanya menggunakan pengembalian eksplisit untuk pengembalian awal . Ruby besar pada kode minimalisme / sihir implisit.

Yang mengatakan, jika pengembalian eksplisit akan membuat segalanya lebih jelas, atau lebih mudah dibaca, itu tidak akan membahayakan apa pun.


116
Apa gunanya kode minimalis jika menghasilkan kebingungan maksimal?
jononomo

@ JonCrowell Alih-alih seharusnya menghasilkan maksimalisasi dokumentasi.
jazzpi

29
@jazzpi Jika Anda harus sangat mendokumentasikan kode Anda agar dapat dipahami dengan baik Anda tidak berlatih minimalis, Anda berlatih golf kode .
Schwern

2
Itu tidak membingungkan untuk mengecualikan pengembalian akhir, itu membingungkan untuk TERMASUK itu - sebagai pengembang Ruby, returnsudah memiliki makna semantik yang signifikan sebagai EXIT AWAL ke fungsi.
Devon Parsons

14
@DevonParsons Bagaimana bisa seseorang membingungkan sebuah returndi baris terakhir sebagai EXIT AWAL?
DarthPaghius

31

Saya pribadi menggunakan returnkata kunci untuk membedakan antara apa yang saya sebut metode fungsional , yaitu metode yang dieksekusi terutama untuk nilai pengembaliannya, dan metode prosedural yang dieksekusi terutama untuk efek sampingnya. Jadi, metode di mana nilai kembali itu penting, dapatkan returnkata kunci tambahan untuk menarik perhatian pada nilai balik.

Saya menggunakan perbedaan yang sama ketika memanggil metode: metode fungsional mendapatkan tanda kurung, metode prosedural tidak.

Dan last but not least, saya juga menggunakan perbedaan itu dengan blok: blok fungsional mendapatkan kurung kurawal, blok prosedural (yaitu blok yang "melakukan" sesuatu) dapatkan do/ end.

Namun, saya mencoba untuk tidak menjadi religius tentang hal itu: dengan balok, kurung kurawal dan do/ endmemiliki prioritas yang berbeda, dan alih-alih menambahkan tanda kurung eksplisit untuk mengaburkan ekspresi, saya hanya beralih ke gaya lain. Hal yang sama berlaku untuk pemanggilan metode: jika menambahkan tanda kurung di sekitar daftar parameter membuat kode lebih mudah dibaca, saya melakukannya, bahkan jika metode tersebut bersifat prosedural.


1
Saat ini saya menghilangkan pengembalian dalam "one-liners" apa pun, yang mirip dengan apa yang Anda lakukan, dan saya melakukan semuanya dengan cara yang sama. Senang mengetahui bahwa saya tidak sepenuhnya menyukai gaya saya. :)
Sasha Chedygov

@ Jorg: Apakah Anda menggunakan pengembalian lebih awal dalam kode Anda? Apakah itu menyebabkan kebingungan dengan skema prosedural / fungsional Anda?
Andrew Grimm

1
@Andrew Grimm: Ya, dan Ya. Saya sedang berpikir untuk membalikkannya. Sebagian besar metode saya bersifat transparan / murni / fungsional / apa pun yang ingin Anda panggil, jadi masuk akal untuk menggunakan formulir yang lebih pendek di sana. Dan metode yang ditulis dalam gaya fungsional cenderung tidak memiliki pengembalian awal, karena itu menyiratkan pengertian "langkah", yang tidak terlalu fungsional. Meskipun, saya tidak suka pengembalian di tengah. Saya menggunakannya pada awalnya sebagai penjaga atau pada akhirnya untuk menyederhanakan aliran kontrol.
Jörg W Mittag

Jawaban yang bagus, tetapi sebenarnya lebih baik memanggil metode prosedural dengan () dan metode fungsional tanpa - lihat jawaban saya di bawah ini untuk alasannya
Alex Dean

13

Sebenarnya yang penting adalah membedakan antara:

  1. Fungsi - metode yang dieksekusi untuk nilai pengembaliannya
  2. Prosedur - metode yang dilakukan untuk efek sampingnya

Ruby tidak memiliki cara asli untuk membedakan ini - yang membuat Anda rentan untuk menulis prosedur side_effect()dan pengembang lain memutuskan untuk menyalahgunakan nilai balik implisit dari prosedur Anda (pada dasarnya memperlakukannya sebagai fungsi tidak murni).

Untuk mengatasinya, keluarkan daun dari buku Scala dan Haskell dan minta prosedur Anda dikembalikan secara eksplisit nil(alias Unitatau ()dalam bahasa lain).

Jika Anda mengikuti ini, maka menggunakan returnsintaksis eksplisit atau tidak hanya menjadi masalah gaya pribadi.

Untuk membedakan lebih jauh antara fungsi dan prosedur:

  1. Salin ide bagus Jörg W Mittag untuk menulis blok fungsional dengan kurung kurawal, dan blok prosedural dengan do/end
  2. Saat Anda menjalankan prosedur, gunakan (), sedangkan saat Anda menjalankan fungsi, jangan

Perhatikan bahwa Jörg W Mittag sebenarnya menganjurkan sebaliknya - menghindari ()untuk prosedur - tetapi itu tidak dianjurkan karena Anda ingin invokasi metode efek samping secara jelas dapat dibedakan dari variabel, terutama ketika arity adalah 0. Lihat panduan gaya Scala tentang metode doa untuk detail.


Jika saya memerlukan nilai yang dikembalikan dari function_a, dan function_aditulis untuk menelepon (dan hanya dapat beroperasi dengan menelepon) procedure_b, apakah function_abenar-benar sebuah fungsi? Ini mengembalikan nilai, memenuhi kebutuhan Anda untuk fungsi, tetapi memiliki efek samping, memenuhi persyaratan prosedur.
Devon Parsons

2
Anda benar Devon - function_abisa berisi pohon procedure_...panggilan, karena memang procedure_abisa berisi pohon function_...panggilan. Seperti Scala tetapi tidak seperti Haskell, tidak ada jaminan di Ruby bahwa suatu fungsi tidak berefek samping.
Alex Dean

8

Panduan gaya menyatakan, bahwa Anda tidak boleh menggunakan returnpada pernyataan terakhir Anda. Anda masih dapat menggunakannya jika itu bukan yang terakhir . Ini adalah salah satu konvensi yang diikuti oleh komunitas dengan ketat, dan Anda juga harus jika Anda berencana untuk berkolaborasi dengan siapa pun yang menggunakan Ruby.


Yang sedang berkata, argumen utama untuk menggunakan returns eksplisit adalah bahwa itu membingungkan bagi orang-orang yang datang dari bahasa lain.

  • Pertama, ini tidak sepenuhnya eksklusif untuk Ruby. Misalnya Perl juga memiliki pengembalian implisit.
  • Kedua, mayoritas orang yang menerapkan ini berasal dari bahasa Algol. Sebagian besar cara "tingkat lebih rendah" dari Ruby, maka Anda harus menulis lebih banyak kode untuk menyelesaikan sesuatu.

Heuristik umum untuk panjang metode (tidak termasuk getter / setter) di java adalah satu layar . Dalam hal ini, Anda mungkin tidak melihat definisi metode dan / atau sudah lupa dari mana Anda kembali.

Di sisi lain, di Ruby yang terbaik adalah tetap menggunakan metode yang panjangnya kurang dari 10 baris . Mengingat itu, orang akan bertanya-tanya mengapa dia harus menulis ~ 10% lebih banyak pernyataan, ketika mereka jelas tersirat.


Karena Ruby tidak memiliki metode batal dan semuanya jauh lebih ringkas, Anda hanya menambahkan overhead untuk tidak ada manfaat jika Anda menggunakan returns eksplisit .


1
"Ini adalah salah satu konvensi yang diikuti oleh komunitas dengan ketat" Pengalaman saya adalah tidak, orang tidak mengikuti ini dengan ketat kecuali mereka adalah pengembang Ruby yang sangat berpengalaman.
Tim Holt

1
@TimHolt Saya pasti sangat beruntung saat itu. (:
ndnenkov

1
@ Dan sepenuhnya setuju. Yang penting adalah bahwa ketika sebuah metode (atau kelas) mulai melebihi 5/100 baris, maka itu ide yang baik untuk mempertimbangkan kembali apa yang ingin Anda capai. Really Sandi's Rules adalah kerangka kerja kognitif untuk memikirkan kode yang bertentangan dengan dogma yang sewenang-wenang. Mayoritas kode yang saya temui tidak bermasalah karena secara artifisial dibatasi - umumnya berlawanan - kelas dengan banyak tanggung jawab, metode yang lebih panjang daripada banyak kelas. Pada dasarnya terlalu banyak orang "melompat hiu" - memadukan tanggung jawab.
Brian Dear

1
Terkadang konvensi masih merupakan ide yang buruk. Ini adalah konvensi gaya yang bergantung pada sihir, membuat tipe pengembalian sulit ditebak. tidak menawarkan keuntungan selain menyimpan 7 penekanan tombol dan tidak sesuai dengan hampir semua bahasa lainnya. Jika Ruby pernah melakukan konsolidasi Python 3 gaya dan penyederhanaan lulus, saya berharap implisit apapun adalah yang pertama di talenan.
Shayne

2
Kecuali jelas itu sebenarnya tidak membuat hal-hal lebih mudah dibaca! Keajaiban dan kelengkapan bukanlah hal yang sama.
Shayne

4

Saya setuju dengan Ben Hughes dan tidak setuju dengan Tim Holt, karena pertanyaannya menyebutkan cara definitif Python melakukannya dan bertanya apakah Ruby memiliki standar yang sama.

Itu benar.

Ini adalah fitur bahasa yang sangat terkenal sehingga siapa pun yang diharapkan men-debug masalah di ruby ​​harus diharapkan untuk mengetahuinya.


3
Saya setuju dengan Anda secara teori, tetapi dalam praktiknya, programmer membuat kesalahan dan mengacaukannya. Contohnya, kita semua tahu bahwa Anda menggunakan "==" dalam kondisi dan bukan "=", tetapi kami SEMUA telah membuat kesalahan itu sebelumnya dan tidak menyadarinya sampai nanti.
Tim Holt

1
@TimHolt Saya tidak menangani kasus penggunaan tertentu, saya hanya menjawab pertanyaan. Ini adalah gaya yang secara eksplisit buruk, seperti yang dipilih oleh komunitas, untuk menggunakan returnkata kunci ketika tidak perlu.
Devon Parsons

3
Cukup adil. Saya mengatakan bahwa gaya yang Anda sebutkan dapat menyebabkan kesalahan jika Anda tidak berhati-hati. Berdasarkan pengalaman saya, saya pribadi menganjurkan gaya yang berbeda.
Tim Holt

1
@TimHolt Ngomong-ngomong, pertanyaan ini ditanyakan sebelum panduan gaya ditulis, jadi jawaban yang tidak setuju tidak selalu salah, hanya ketinggalan jaman. Saya tersandung di sini entah bagaimana dan tidak melihat tautan yang saya berikan, jadi saya pikir orang lain mungkin berakhir di sini juga.
Devon Parsons

1
Ini bukan "standar" resmi. Panduan gaya ruby ​​yang digunakan oleh rubocop dan dihubungkan di atas dengan klaim di sana oleh Devon Parsons, dibuat oleh peretas ruby ​​non-inti. Jadi komentar Devon sebenarnya salah. Tentu saja Anda MASIH dapat menggunakannya tetapi ini bukan standar.
shevy

1

Ini masalah gaya mana yang paling Anda sukai. Anda harus menggunakan kata kunci kembali jika Anda ingin kembali dari suatu tempat di tengah metode.


0

Seperti kata Ben. Fakta bahwa 'nilai kembali metode ruby ​​adalah nilai balik dari pernyataan terakhir di badan fungsi' menyebabkan kata kunci kembali jarang digunakan dalam sebagian besar metode ruby.

def some_func_which_returns_a_list( x, y, z)
  return nil if failed_some_early_check


  # function code 

  @list     # returns the list
end

4
Anda juga bisa menulis "return nil if fail_some_early_check" jika niat Anda masih jelas
Mike Woodhouse

@ Gishu Saya benar-benar mengeluh tentang pernyataan if, bukan nama metode.
Andrew Grimm

@Andrew .. oh ujung yang hilang :). Mengerti. tetap untuk menenangkan komentar lain juga dari Mike.
Gishu

Mengapa tidak if failed_check then nil else @list end? Itu benar-benar fungsional .
cdunn2001

@ cdunn2001 Tidak jelas mengapa jika kalau begitu fungsional .. alasan gaya ini adalah mengurangi bersarang melalui pintu keluar awal. IMHO juga lebih mudah dibaca.
Gishu
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.