Mengapa Ruby tidak mendukung i ++ atau i-- (operator increment / decrement)?

130

Operator kenaikan / penurunan pra / pasca ( ++dan --) adalah sintaksis bahasa pemrograman yang cukup standar (setidaknya untuk bahasa prosedural dan berorientasi objek).

Mengapa Ruby tidak mendukung mereka? Saya mengerti Anda dapat mencapai hal yang sama dengan +=dan -=, tetapi anehnya rasanya untuk mengecualikan sesuatu seperti itu, terutama karena itu begitu ringkas dan konvensional.

Contoh:

i = 0    #=> 0
i += 1   #=> 1
i        #=> 1
i++      #=> expect 2, but as far as I can tell, 
         #=> irb ignores the second + and waits for a second number to add to i

Saya mengerti Fixnumtidak dapat diubah, tetapi jika +=hanya bisa membuat yang baru Fixnumdan mengaturnya, mengapa tidak melakukan hal yang sama ++?

Apakah konsistensi dalam tugas yang mengandung =karakter satu-satunya alasan untuk ini, atau apakah saya kehilangan sesuatu?

Andy_Vulhop
sumber
2
Grep kode sumber ruby ​​untuk operator tersebut. Jika tidak ada - Matz tidak menyukainya.
Eimantas
Anda tidak dapat melakukan preincrement dengan +=operator. Dalam CI coba gunakan ++/ --hanya di dalam kondisional, lebih suka untuk yang lebih literal +=/ -=dalam pernyataan dasar. Mungkin karena saya belajar Python (lama setelah C ...)
Nick T
Bukankah ada pertanyaan seperti ini untuk Python kemarin?
BoltClock
@ Eimantas jelas pencipta bahasa tidak menyukai mereka. Terlalu umum untuk diabaikan. Saya bertanya-tanya MENGAPA, yang agaknya telah diklarifikasi dengan jawaban di bawah ini.
Andy_Vulhop
1
Saya pikir ini (hampir) model pertanyaan SO. Ini bukan sesuatu yang tidak mudah di-googleable untuk mendapat balasan yang dipertimbangkan. Cukup jelas dan spesifik dalam jawaban apa yang diperlukan dan jawaban itu menjelaskan aspek pemrograman yang dapat membuat orang berpikir lebih luas dari sekadar inti pertanyaan.
PurplePilot

Jawaban:

97

Inilah cara Matz (Yukihiro Matsumoto) menjelaskannya di utas lama :

Hi,

In message "[ruby-talk:02706] X++?"
    on 00/05/10, Aleksi Niemelä <[email protected]> writes:

|I got an idea from http://www.pragprog.com:8080/rubyfaq/rubyfaq-5.html#ss5.3
|and thought to try. I didn't manage to make "auto(in|de)crement" working so
|could somebody help here? Does this contain some errors or is the idea
|wrong?

  (1) ++ and -- are NOT reserved operator in Ruby.

  (2) C's increment/decrement operators are in fact hidden assignment.
      They affect variables, not objects.  You cannot accomplish
      assignment via method.  Ruby uses +=/-= operator instead.

  (3) self cannot be a target of assignment.  In addition, altering
      the value of integer 1 might cause severe confusion throughout
      the program.

                            matz.
Brian
sumber
10
2 dan 3 tampaknya saling bertentangan. Jika penugasan diri buruk, mengapa +=/ -=ok? Dan tidak 1+=1akan seburuk itu? (Gagal di IRB dengan syntax error, unexpected ASSIGNMENT)
Andy_Vulhop
2
(2) berarti bahwa dalam C, Anda tidak mengubah nilai itu sendiri ... Anda mengubah isi variabel yang menyimpan nilai. Itu agak terlalu meta untuk bahasa apa pun yang melewati nilai. Kecuali ada cara untuk melewatkan sesuatu dengan referensi di Ruby (dan maksud saya benar-benar "dengan referensi", tidak melewati referensi dengan nilai), mengubah variabel itu sendiri tidak akan mungkin dalam suatu metode.
cHao
5
Mungkin saya kehilangan sesuatu di sini. +=mengganti objek referensi variabel dengan objek yang sama sekali baru. Anda dapat memeriksa ini dengan menelepon i.object_idsebelum dan sesudah i+=1. Mengapa itu secara teknis lebih sulit dilakukan ++?
Andy_Vulhop
6
@Andy_Vulhop: # 3 menjelaskan mengapa secara teknis tidak mungkin untuk penugasan menjadi metode, bukan mengapa penugasan tidak mungkin secara umum (poster Matz menjawab untuk berpikir mungkin untuk membuat ++metode).
Chuck
2
Di Ruby semua literal juga objek. Jadi saya percaya Matz mencoba untuk mengatakan bahwa dia tidak yakin bahwa dia menyukai gagasan berurusan dengan 1 ++ sebagai pernyataan. Secara pribadi saya pikir ini tidak masuk akal karena sebagai @Andy_Vulhop mengatakan 1 + = 2 sama seperti wack, dan Ruby hanya menimbulkan kesalahan ketika Anda melakukan ini. Jadi 1 ++ tidak sulit untuk ditangani. Mungkin kebutuhan parser untuk mengatasi gula sintaksis semacam itu tidak diinginkan.
Steve Midgley
28

Salah satu alasannya adalah bahwa hingga sekarang setiap operator penugasan (yaitu operator yang mengubah variabel) ada =di dalamnya. Jika Anda menambahkan ++dan --, itu tidak lagi terjadi.

Alasan lainnya adalah karena perilaku ++dan --kerap membingungkan orang. Contoh kasus: Nilai pengembalian i++dalam contoh Anda sebenarnya adalah 1, bukan 2 (nilai baru yaitu i2, namun).

sepp2k
sumber
4
Lebih dari alasan lain sejauh ini, rasional bahwa "semua tugas ada =di dalamnya" tampaknya masuk akal. Saya bisa menghargai itu sebagai kepatuhan yang kuat terhadap konsistensi.
Andy_Vulhop
bagaimana dengan ini: a.kapitalisasi! (penugasan implisit dari a)
Luís Soares
1
@ LuísSoares a.capitalize!tidak menetapkan ulang a, itu akan mengubah string yang amerujuk. Referensi lain ke string yang sama akan terpengaruh dan jika Anda melakukannya a.object_idsebelum dan sesudah panggilan ke capitalize, Anda akan mendapatkan hasil yang sama (tidak ada yang benar jika Anda melakukannya a = a.capitalize).
sepp2k
1
@ LuísSoares Seperti yang saya katakan, a.capitalize!akan mempengaruhi referensi lain ke string yang sama. Itu perbedaan yang sangat praktis. Sebagai contoh jika Anda memiliki def yell_at(name) name.capitalize!; puts "HEY, #{name}!" enddan kemudian memanggilnya seperti ini:, my_name = "luis"; yell_at(my_name)nilai my_namesekarang akan menjadi "LUIS", sedangkan itu tidak akan terpengaruh jika Anda telah menggunakan capitalizedan tugas.
sepp2k
1
Wow. Itu menakutkan ... Mengetahui bahwa di Jawa string tidak dapat diubah .. Tetapi dengan kekuatan datang tanggung jawab. Terima kasih untuk penjelasannya.
Luís Soares
25

Ini tidak konvensional dalam bahasa OO. Bahkan, tidak ada ++dalam Smalltalk, bahasa yang menciptakan istilah "pemrograman berorientasi objek" (dan bahasa Ruby paling kuat dipengaruhi oleh). Apa yang Anda maksud adalah bahwa itu konvensional dalam bahasa C dan meniru C. Ruby memang memiliki sintaksis mirip-C, tetapi itu tidak sopan dalam mengikuti tradisi C.

Adapun mengapa itu tidak ada di Ruby: Matz tidak menginginkannya. Itu benar-benar alasan utama.

Alasan tidak ada hal seperti itu ada di Smalltalk adalah karena bagian itu dari bahasa override filosofi yang menugaskan variabel secara fundamental berbeda jenis hal daripada mengirim pesan ke objek - itu pada tingkat yang berbeda. Pemikiran ini mungkin memengaruhi Matz dalam mendesain Ruby.

Tidaklah mustahil untuk memasukkannya ke dalam Ruby - Anda dapat dengan mudah menulis preprocessor yang mengubah semuanya ++menjadi +=1. tetapi jelas Matz tidak menyukai gagasan tentang operator yang melakukan "tugas tersembunyi." Tampaknya juga agak aneh memiliki operator dengan operan integer tersembunyi di dalamnya. Tidak ada operator lain dalam bahasa yang berfungsi seperti itu.

Membuang
sumber
1
Saya tidak berpikir Anda saran preprosesor akan bekerja; (bukan ahli) tapi saya pikir saya = 42, i ++ akan mengembalikan 42 di mana saya + = 1 akan kembali 43. Apakah saya salah dalam hal ini? Jadi saran Anda dalam hal ini adalah untuk menggunakan i ++ sebagai ++ i biasanya digunakan yang imho sangat buruk dan dapat menyebabkan lebih banyak ruginya daripada kebaikan.
AturSams
12

Saya pikir ada alasan lain: ++di Ruby tidak akan jauh berguna seperti di C dan penggantinya langsung.

Alasannya, forkata kunci: sementara itu penting dalam C, sebagian besar berlebihan di Ruby. Sebagian besar iterasi di Ruby dilakukan melalui metode Enumerable, seperti eachdan mapketika iterasi melalui beberapa struktur data, dan Fixnum#timesmetode, ketika Anda perlu mengulang beberapa kali.

Sebenarnya, sejauh yang saya lihat, sebagian besar waktu +=1digunakan oleh orang yang baru saja pindah ke Ruby dari bahasa C-style.

Singkatnya, itu benar-benar dipertanyakan apakah metode ++dan --akan digunakan sama sekali.

Mladen Jablanović
sumber
1
Ini jawaban yang terbaik. ++ sering digunakan untuk iterasi. Ruby tidak menganjurkan jenis iterasi ini.
AturSams
3

Saya pikir alasan Matz untuk tidak menyukai mereka adalah bahwa itu sebenarnya menggantikan variabel dengan yang baru.

ex:

a = SomeClass.new
def a.go
  'Halo'
akhir
# pada titik ini, Anda dapat menghubungi a.go
# tetapi jika Anda melakukan a ++
# Itu benar-benar berarti a = a +1
# jadi Anda tidak bisa lagi menelepon a.go
# karena Anda kehilangan orisinal Anda

Sekarang, jika seseorang dapat meyakinkannya bahwa itu harus memanggil #succ! atau apa yang tidak, itu akan lebih masuk akal, dan menghindari masalah. Anda dapat menyarankannya pada inti ruby.

rogerdpack
sumber
9
"Anda dapat menyarankannya pada inti ruby" ... Setelah Anda membaca dan memahami argumen di semua utas lainnya di mana disarankan terakhir kali, dan waktu sebelum itu, dan waktu sebelum itu, dan waktu sebelum itu, dan waktu sebelum itu, dan ... Saya belum lama berada di komunitas Ruby, tetapi hanya selama waktu saya, saya ingat setidaknya dua puluh diskusi seperti itu.
Jörg W Mittag
3

Anda dapat menentukan .+operator peningkatan-diri:

class Variable
  def initialize value = nil
    @value = value
  end
  attr_accessor :value
  def method_missing *args, &blk
    @value.send(*args, &blk)
  end
  def to_s
    @value.to_s
  end

  # pre-increment ".+" when x not present
  def +(x = nil)
    x ? @value + x : @value += 1
  end
  def -(x = nil)
    x ? @value - x : @value -= 1
  end
end

i = Variable.new 5
puts i                #=> 5

# normal use of +
puts i + 4            #=> 9
puts i                #=> 5

# incrementing
puts i.+              #=> 6
puts i                #=> 6

Informasi lebih lanjut tentang "class Variable" tersedia di " Class Variable to increment object Fixnum ".

Sony Santos
sumber
2

Dan dalam kata-kata David Black dari bukunya "The Well-Grounded Rubyist":

Beberapa objek di Ruby disimpan dalam variabel sebagai nilai langsung. Ini termasuk bilangan bulat, simbol (yang terlihat seperti: ini), dan objek khusus benar, salah, dan nihil. Ketika Anda menetapkan salah satu dari nilai-nilai ini ke variabel (x = 1), variabel tersebut menyimpan nilai itu sendiri, bukan referensi untuk itu. Dalam istilah praktis, ini tidak masalah (dan akan sering dibiarkan tersirat, alih-alih dijabarkan berulang kali, dalam diskusi referensi dan topik terkait dalam buku ini). Ruby menangani referensi objek referensi secara otomatis; Anda tidak perlu melakukan pekerjaan tambahan untuk mengirim pesan ke objek yang berisi, katakanlah, referensi ke string, sebagai lawan dari objek yang berisi nilai integer langsung. Tetapi aturan representasi nilai langsung memiliki beberapa konsekuensi menarik, terutama ketika datang ke bilangan bulat. Untuk satu hal, objek apa pun yang direpresentasikan sebagai nilai langsung selalu merupakan objek yang persis sama, tidak peduli berapa banyak variabel yang ditugaskan padanya. Hanya ada satu objek 100, hanya satu objek yang salah, dan sebagainya. Sifat langsung, unik dari variabel integer-terikat adalah di belakang kurangnya Ruby operator pre dan post-increment — artinya, Anda tidak dapat melakukan ini di Ruby: x = 1 x ++ # Tidak ada operator seperti itu Alasannya adalah karena untuk kehadiran langsung 1 dalam x, x ++ akan seperti 1 ++, yang berarti Anda akan mengubah angka 1 ke angka 2 — dan itu tidak masuk akal. tidak peduli berapa banyak variabel yang ditugaskan padanya. Hanya ada satu objek 100, hanya satu objek yang salah, dan sebagainya. Sifat langsung, unik dari variabel integer-terikat adalah di belakang kurangnya Ruby operator pre dan post-increment — artinya, Anda tidak dapat melakukan ini di Ruby: x = 1 x ++ # Tidak ada operator seperti itu Alasannya adalah karena untuk kehadiran langsung 1 dalam x, x ++ akan seperti 1 ++, yang berarti Anda akan mengubah angka 1 ke angka 2 — dan itu tidak masuk akal. tidak peduli berapa banyak variabel yang ditugaskan padanya. Hanya ada satu objek 100, hanya satu objek yang salah, dan sebagainya. Sifat langsung, unik dari variabel integer-terikat adalah di belakang kurangnya Ruby operator pre dan post-increment — artinya, Anda tidak dapat melakukan ini di Ruby: x = 1 x ++ # Tidak ada operator seperti itu Alasannya adalah karena untuk kehadiran langsung 1 dalam x, x ++ akan seperti 1 ++, yang berarti Anda akan mengubah angka 1 ke angka 2 — dan itu tidak masuk akal.

Alexander Swann
sumber
Tapi kenapa Anda bisa melakukan "1.next"?
Magne
1

Tidak bisakah ini dicapai dengan menambahkan metode baru ke kelas fixnum atau Integer?

$ ruby -e 'numb=1;puts numb.next'

mengembalikan 2

Metode "Merusak" tampaknya ditambahkan !untuk memperingatkan kemungkinan pengguna, jadi menambahkan metode baru yang disebut next!akan cukup banyak melakukan apa yang diminta yaitu.

$ ruby -e 'numb=1; numb.next!; puts numb' 

mengembalikan 2 (karena mati rasa telah bertambah)

Tentu saja, next!metode harus memeriksa bahwa objek adalah variabel integer dan bukan bilangan real, tetapi ini harus tersedia.

Sjerek
sumber
1
Integer#nextsudah ada (kurang lebih), kecuali disebut Integer#succsebaliknya (untuk 'penerus'). Tetapi Integer#next!(atau Integer#succ!) akan menjadi omong kosong: ingat bahwa metode bekerja pada objek , bukan variabel , jadi numb.next!akan persis sama dengan 1.next!, yang bisa dikatakan, itu akan bermutasi 1 menjadi sama dengan 2 . ++akan sedikit lebih baik karena bisa menjadi gula sintaksis untuk tugas, tetapi secara pribadi saya lebih suka sintaksis saat ini di mana semua tugas dilakukan dengan =.
philomory
Untuk melengkapi komentar di atas: dan Integer#preduntuk mengambil pendahulunya.
Yoni
-6

Periksa operator ini dari C-family di irb Ruby dan ujilah sendiri:

x = 2    # x is 2
x += 2   # x is 4
x++      # x is now 8
++x      # x reverse to 4
Aung Zan Baw
sumber
3
Ini jelas salah dan tidak berfungsi, seperti (x++)pernyataan yang tidak valid di Ruby.
anothermh