Ruby neden i ++ veya i-- (artırma / azaltma operatörlerini) desteklemiyor?


130

Ön / son artırma / azaltma operatörü ( ++ve --) oldukça standart programlama dili sözdizimidir (en azından prosedürel ve nesne yönelimli diller için).

Ruby neden onları desteklemiyor? Sana aynı şeyi başarabileceğini anlamaya +=ve -=, ama sadece o kadar özlü ve konvansiyonel, özellikle beri böyle bir şey dışlamak için tuhaf keyfi görünmektedir.

Misal:

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

Anladığım kadarıyla Fixnumdeğişmez, ama +=yenisini deneyip kurabiliyorsa Fixnum, neden aynısını yapmayalım ++?

=Bunun tek nedeni karakteri içeren ödevlerde tutarlılık mı yoksa bir şeyi mi kaçırıyorum?


2
Bu tür operatörler için Grep ruby ​​kaynak kodu. Hiç yoksa - Matz onlardan hoşlanmaz.
Eimantas

Bir +=operatörle ön artırma yapamazsınız . CI'da ++/ --yalnızca koşul ifadelerini kullanmaya çalışın , daha gerçek olanı +=/ -=temel bir ifadede tercih edin. Muhtemelen Python'u öğrendiğim için (C'den çok sonra ...)
Nick T

Dün Python için buna benzer bir soru yok muydu?
BoltClock

@Eimant, açıkçası dilin yaratıcıları onlardan hoşlanmadı. Göz ardı etmek çok yaygın. Aşağıdaki cevaplarla bir şekilde açıklığa kavuşturulan NEDEN merak ediyordum.
Andy_Vulhop

1
Bunun (neredeyse) bir model SO sorusu olduğunu düşünüyorum. Göz önünde bulundurulacak bir cevap almak kolay bir şekilde google'da yapılamayacak bir şey değildir. Hangi cevabın gerekli olduğu oldukça açık ve spesifiktir ve cevap, kişinin sorunun özünden daha geniş düşünmesini sağlayabilecek bir programlama yönüne ışık tutmaktadır.
PurplePilot

Yanıtlar:


97

Matz (Yukihiro Matsumoto) bunu eski bir başlıkta şöyle açıklıyor :

Hi,

In message "[ruby-talk:02706] X++?"
    on 00/05/10, Aleksi Niemelä <aleksi.niemela@cinnober.com> 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.

10
2 ve 3 çelişkili görünüyor. Öz atama kötüyse, neden +=/ -=tamam mı? Ve bu 1+=1kadar kötü olmaz mıydı? ( syntax error, unexpected ASSIGNMENT
IRB'de

2
(2), C'de değerin kendisini değiştirmediğiniz anlamına gelir ... değeri tutan değişkenin içeriğini değiştiriyorsunuz. Bu, değere göre geçen herhangi bir dil için biraz fazla meta. Ruby'de bir şeyi referans olarak iletmenin bir yolu olmadığı sürece (ve gerçekten "referansla", değere göre bir referansı iletmek değil), değişkenin kendisini değiştirmek bir yöntem içinde mümkün olmazdı.
cHao

5
Belki burada bir şey eksik. +=değişkenin referans verdiği nesneyi tamamen yeni bir nesneyle değiştirir. Bunu i.object_idönce ve sonra arayarak kontrol edebilirsiniz i+=1. Neden bununla teknik olarak daha zor olabilir ki ++?
Andy_Vulhop

6
@Andy_Vulhop: # 3, atamanın bir yöntem olmasının neden teknik olarak imkansız olduğunu açıklıyor, genel olarak atamanın neden imkansız olduğunu değil (Matz posteri, bir ++yöntem yaratmanın mümkün olabileceğini düşündüğünü yanıtlıyordu ).
Chuck

2
Ruby'de tüm değişmez değerler aynı zamanda nesnelerdir. Bu yüzden Matz'ın 1 ++ ile bir ifade olarak uğraşma fikrinden hoşlandığından emin olmadığını söylemeye çalıştığına inanıyorum. Şahsen ben bunun mantıksız olduğunu düşünüyorum, çünkü @Andy_Vulhop'un söylediği gibi 1 + = 2 de tuhaftır ve Ruby bunu yaptığınızda bir hata ortaya çıkarır. Yani 1 ++ 'nın üstesinden gelmesi daha zor değil. Muhtemelen ayrıştırıcının bu tür sözdizimsel şekerle baş etme ihtiyacı arzu edilmez.
Steve Midgley

28

Bunun bir nedeni, şimdiye kadar her atama operatörünün (yani bir değişkeni değiştiren bir operatörün) içinde bir a =sahip olmasıdır. Eğer eklerseniz ++ve --bu artık durum bu.

Diğer bir neden de insanların davranışları ++ve --çoğu zaman kafa karıştırmasıdır. Örnek olay: Örneğinizdeki dönüş değeri i++aslında 2 değil 1 olur (yeni değeri i2 olur).


4
Şimdiye kadarki tüm diğer nedenlerden daha çok, "tüm görevlerin bir içindedir" mantığı mantıklı =görünüyor. Buna tutarlılığa şiddetle bağlılık olarak saygı duyabilirim.
Andy_Vulhop

buna ne dersiniz: a. sermayeyi büyük hale getirin! (a'nın üstü kapalı ataması)
Luís Soares

1
@ LuísSoares a.capitalize!yeniden atama yapmaz a, başvuran dizeyi adeğiştirir. Aynı dizeye yapılan diğer referanslar etkilenecek ve a.object_idçağrılmadan önce ve sonra capitalizeyaparsanız, aynı sonucu alırsınız ( a = a.capitalizebunun yerine yaparsanız hiçbiri doğru olmaz ).
sepp2k

1
@ LuísSoares Dediğim gibi a.capitalize!, aynı dizeye yapılan diğer referansları etkileyecek. Bu çok pratik bir fark. Örneğin, eğer varsa def yell_at(name) name.capitalize!; puts "HEY, #{name}!" endve o zaman bunu şöyle adlandırırsanız:, şimdi my_name = "luis"; yell_at(my_name)değeri my_nameolacak "LUIS", oysa kullanmış olsaydınız capitalizeve bir ödev etkilenmeyecektir .
2016

1
Vay. Bu korkutucu ... Java'da dizelerin değişmez olduğunu bilmek .. Ama güçle birlikte sorumluluk gelir. Açıklama için teşekkürler.
Luís Soares

25

OO dillerinde geleneksel değildir. Aslında, ++Smalltalk'ta "nesne yönelimli programlama" terimini (ve Ruby'nin en güçlü şekilde etkilediği dil) bulan dil yoktur. Demek istediğin, C'de geleneksel olduğu ve C'yi yakından taklit eden diller olduğudur. Ruby'nin bir şekilde C benzeri bir sözdizimi vardır, ancak C geleneklerine bağlı kalarak kölece değildir.

Ruby'de neden olmadığına gelince: Matz istemedi. Bu gerçekten nihai neden.

Smalltalk'ta böyle bir şeyin olmamasının nedeni, dilin bir değişken atamanın temelde bir nesneye mesaj göndermekten farklı bir şey olması felsefesinin bir parçası olmasıdır - farklı bir seviyededir. Bu düşünce muhtemelen Ruby'yi tasarlarken Matz'ı etkiledi.

Bunu Ruby'ye dahil etmek imkansız olmazdı - kolayca hepsini ++dönüştüren bir önişlemci yazabilirsiniz +=1. ama belli ki Matz, "gizli bir görev" yapan bir operatör fikrini beğenmedi. Ayrıca içinde gizli bir tamsayı işleneni olan bir operatörün olması biraz garip görünüyor. Dildeki başka hiçbir operatör bu şekilde çalışmaz.


1
Önişlemci önerisinin işe yarayacağını sanmıyorum; (bir uzman değil) ama bence i = 42, i ++ 42'yi döndürecek ve burada i + = 1 43'ü döndürecektir. Bunda yanlış mıyım? Bu durumda öneriniz, normalde kullanıldığı gibi i ++ i ++ 'ı kullanmak olacaktır ki bu oldukça kötü bir imho ve yarardan çok zarara neden olabilir.
AturSams

12

Bence başka bir neden daha var: ++Ruby'de C ve onun doğrudan halefleri gibi uzaktan kullanışlı olmayacaktı.

Bunun nedeni, foranahtar kelime: C için gerekli olsa da, Ruby'de çoğunlukla gereksizdir. Ruby'deki yinelemenin çoğu, belirli bir sayıda döngü yapmanız gerektiğinde, bazı veri yapıları ve yöntemlerde yinelendiğinde eachve bunlar gibi Numaralandırılabilir yöntemlerle yapılır .mapFixnum#times

Aslında gördüğüm kadarıyla çoğu zaman +=1Ruby'ye C tarzı dillerden yeni göç etmiş insanlar tarafından kullanılıyor.

Yöntemler Kısacası, gerçekten şüpheli olduğunu ++ve --hiç kullanılacaktır.


1
Bu en iyi cevap imho. ++ genellikle yineleme için kullanılır. Ruby bu tür bir yinelemeyi teşvik etmez.
AturSams

3

Bence Matz'ın onları beğenmemesinin nedeni aslında değişkeni yenisiyle değiştirmesidir.

örn:

a = SomeClass.new
def a.go
  'Merhaba'
son
# bu noktada a.go'yu arayabilirsiniz
# ama bir a ++ yaptıysanız
# bu gerçekten a = a + 1 anlamına gelir
# böylece artık a.go'yu arayamazsınız
# aslını kaybettiğin gibi

Şimdi eğer birisi onu # succ diye çağırması gerektiğine ikna edebilirse! ya da ne değil, bu daha mantıklı olur ve problemi önler. Ruby core'da önerebilirsiniz.


9
... "Sen yakut çekirdek üzerinde önerebilirsiniz" sonra okuduğunuzu ve bundan önce daha önce zaman, ondan önce saati ve zamanını bu son kez öne sürüldü tüm diğer konuları argümanları anlaşılmış ve, ve ondan önceki zaman, ve ... Ruby topluluğunda çok uzun süredir bulunmadım, ama sadece kendi zamanımda, bu türden en az yirmi tartışmayı hatırlıyorum.
Jörg W Mittag

3

Kendi .+kendini artırma operatörü tanımlayabilirsiniz :

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

"Sınıf Değişkeni" hakkında daha fazla bilgi " Fixnum nesnelerini artırmak için Sınıf Değişkeni " bölümünde mevcuttur .


2

Ve David Black'in "The Well-Grounded Rubyist" kitabındaki sözleriyle:

Ruby'deki bazı nesneler değişkenler içinde anlık değerler olarak saklanır. Bunlar arasında tamsayılar, semboller (buna benzer) ve true, false ve nil özel nesneleri bulunur. Bu değerlerden birini bir değişkene (x = 1) atadığınızda, değişken bir referans yerine değerin kendisini tutar. Pratik anlamda, bu önemli değildir (ve bu kitaptaki referanslar ve ilgili konuların tartışmalarında tekrar tekrar ifade edilmek yerine genellikle ima edildiği gibi bırakılacaktır). Ruby, nesne referanslarının referanslarının kaldırılmasını otomatik olarak gerçekleştirir; anlık bir tamsayı değeri içeren bir nesnenin aksine, örneğin bir dizgeye referans içeren bir nesneye mesaj göndermek için fazladan bir iş yapmanız gerekmez. Ancak anlık değer gösterimi kuralının birkaç ilginç sonucu vardır. özellikle tamsayılar söz konusu olduğunda. Birincisi, anlık değer olarak gösterilen herhangi bir nesne, kaç değişkene atanmış olursa olsun, her zaman tam olarak aynı nesnedir. Yalnızca bir nesne 100 vardır, yalnızca bir nesne yanlıştır, vb. Tamsayıya bağlı değişkenlerin anlık, benzersiz doğası, Ruby'nin artırma öncesi ve sonrası işleçlerinin olmamasının arkasındadır - yani bunu Ruby'de yapamazsınız: x = 1 x ++ # Böyle bir operatör yok Sebep şu ki x'de 1'in hemen var olması durumunda, x ++ 1 ++ gibi olur, bu da 1 rakamını 2 rakamına değiştireceğiniz anlamına gelir - ve bu hiç mantıklı değil. kaç değişkene atanmış olursa olsun. Yalnızca bir nesne 100 vardır, yalnızca bir nesne yanlıştır, vb. Tamsayıya bağlı değişkenlerin anlık, benzersiz doğası, Ruby'nin artırma öncesi ve sonrası işleçlerinin olmamasının arkasındadır - yani bunu Ruby'de yapamazsınız: x = 1 x ++ # Böyle bir operatör yok Sebep şu ki x'de 1'in hemen var olması durumunda, x ++ 1 ++ gibi olur, bu da 1 rakamını 2 rakamına değiştireceğiniz anlamına gelir - ve bu hiç mantıklı değil. kaç değişkene atanmış olursa olsun. Yalnızca bir nesne 100 vardır, yalnızca bir nesne yanlıştır, vb. Tamsayıya bağlı değişkenlerin anlık, benzersiz doğası, Ruby'nin artırma öncesi ve sonrası işleçlerinin olmamasının arkasındadır - yani bunu Ruby'de yapamazsınız: x = 1 x ++ # Böyle bir operatör yok Sebep şu ki x'de 1'in hemen var olması durumunda, x ++ 1 ++ gibi olur, bu da 1 rakamını 2 rakamına değiştireceğiniz anlamına gelir - ve bu hiç mantıklı değil.


Ama nasıl olur da "1.sonra" yapabilirsin?
Magne

1

Fixnum veya Integer sınıfına yeni bir yöntem eklenerek bu başarılamaz mı?

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

2 döndürür

!Olası kullanıcıları uyarmak için "yıkıcı" yöntemler eklenmiş gibi görünmektedir , bu nedenle adı verilen yeni bir yöntemin eklenmesi, yani next!istenen şeyi hemen hemen yapacaktır.

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

2 döndürür (uyuşma arttığından beri)

Elbette, next!yöntemin nesnenin bir tam sayı değişkeni olduğunu ve gerçek bir sayı olmadığını kontrol etmesi gerekir , ancak bu mevcut olmalıdır .


1
Integer#nextzaten var (az ya da çok), ancak onun Integer#succyerine çağrılması ('halef' için). Ama Integer#next!(veya Integer#succ!) saçma olurdu: yöntemlerin değişkenler üzerinde değil nesneler üzerinde çalıştığını hatırlayın , bu nedenle tam olarak eşit olur , yani 1'i 2'ye eşit olacak şekilde değiştirir . bir ödev için sözdizimsel şeker olabileceği için marjinal olarak daha iyi olurdu, ancak kişisel olarak tüm görevlerin yapıldığı mevcut sözdizimini tercih ederim . numb.next!1.next!++=
philomory

Yukarıdaki yorumu tamamlamak için: ve Integer#predöncülü geri almak için.
Yoni

-6

Ruby's irb'deki C ailesinden bu operatörleri kontrol edin ve bunları kendiniz test edin:

x = 2    # x is 2
x += 2   # x is 4
x++      # x is now 8
++x      # x reverse to 4

3
Bu açıkça yanlıştır ve (x++)Ruby'de geçersiz bir ifade olduğu gibi çalışmaz .
anothermh
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.