Warum unterstützt Ruby i ++ oder i-- nicht (Inkrementierungs- / Dekrementierungsoperatoren)?

130

Der Pre / Post-Inkrement / Dekrement-Operator ( ++und --) ist eine ziemlich standardmäßige Programmiersprachen-Syntax (zumindest für prozedurale und objektorientierte Sprachen).

Warum unterstützt Ruby sie nicht? Ich verstehe, dass Sie mit +=und dasselbe erreichen können -=, aber es scheint seltsamerweise willkürlich, so etwas auszuschließen, zumal es so prägnant und konventionell ist.

Beispiel:

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

Ich verstehe, Fixnumist unveränderlich, aber wenn +=man nur ein neues instanziieren Fixnumund einstellen kann, warum nicht das Gleiche für ++?

Ist die Konsistenz bei Aufgaben, die den =Charakter enthalten, der einzige Grund dafür, oder fehlt mir etwas?

Andy_Vulhop
quelle
2
Grep Ruby-Quellcode für solche Operatoren. Wenn es keine gibt - Matz mag sie nicht.
Eimantas
Sie können mit einem +=Operator keine Vorinkrementierung durchführen . Versuchen Sie in CI, ++/ --nur innerhalb von Bedingungen zu verwenden, und bevorzugen Sie das wörtlichere +=/ -=in einer grundlegenden Anweisung. Wahrscheinlich, weil ich Python gelernt habe (allerdings lange nach C ...)
Nick T
Gab es gestern nicht eine solche Frage für Python?
BoltClock
@Eimantas gut offensichtlich haben die Schöpfer der Sprache sie nicht gemocht. Es ist zu häufig, um es zu übersehen. Ich habe mich gefragt, WARUM, was durch die folgenden Antworten etwas verdeutlicht wurde.
Andy_Vulhop
1
Ich denke, das ist (fast) eine vorbildliche SO-Frage. Es ist nicht leicht, eine überlegte Antwort zu erhalten. Es ist ziemlich klar und spezifisch, welche Antwort erforderlich ist, und die Antwort beleuchtet eine Facette der Programmierung, die dazu führen kann, dass man breiter denkt als nur der Kern der Frage.
PurplePilot

Antworten:

97

So erklärt es Matz (Yukihiro Matsumoto) in einem alten Thread :

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
quelle
10
2 und 3 scheinen widersprüchlich. Wenn die Selbstzuweisung schlecht ist, warum sind +=/ -=ok? Und wäre das nicht 1+=1genauso schlimm? (Es schlägt im IRB mit fehl syntax error, unexpected ASSIGNMENT)
Andy_Vulhop
2
(2) bedeutet, dass Sie in C den Wert selbst nicht ändern ... Sie ändern den Inhalt der Variablen, die den Wert enthält. Das ist ein bisschen zu meta für jede Sprache, die nach Wert geht. Wenn es in Ruby keine Möglichkeit gibt, etwas als Referenz zu übergeben (und ich meine wirklich "als Referenz", ohne eine Referenz als Wert zu übergeben), wäre eine Änderung der Variablen selbst innerhalb einer Methode nicht möglich.
CHao
5
Vielleicht fehlt mir hier etwas. +=Ersetzt das Objekt, auf das die Variable verweist, durch ein ganz neues Objekt. Sie können dies überprüfen, indem Sie i.object_idvorher und nachher anrufen i+=1. Warum sollte das technisch schwieriger sein ++?
Andy_Vulhop
6
@Andy_Vulhop: # 3 erklärt, warum es technisch unmöglich ist, eine Methode zuzuweisen, und nicht, warum eine Zuweisung im Allgemeinen unmöglich ist (das Poster, auf das Matz geantwortet hat, dachte, es sei möglich, eine ++Methode zu erstellen ).
Chuck
2
In Ruby sind alle Literale auch Objekte. Ich glaube, Matz versucht zu sagen, dass er nicht sicher ist, ob er die Idee, mit 1 ++ als Aussage umzugehen, mag. Persönlich halte ich dies für unvernünftig, da, wie @Andy_Vulhop sagt, 1 + = 2 genauso verrückt ist und Ruby nur einen Fehler auslöst, wenn Sie dies tun. 1 ++ ist also nicht schwerer zu handhaben. Möglicherweise ist die Notwendigkeit des Parsers, mit dieser Art von syntaktischem Zucker umzugehen, unerwünscht.
Steve Midgley
28

Ein Grund ist, dass bisher jeder Zuweisungsoperator (dh ein Operator, der eine Variable ändert) einen enthält =. Wenn Sie ++und hinzufügen --, ist dies nicht mehr der Fall.

Ein weiterer Grund ist, dass das Verhalten von Menschen ++und --oft verwirrt. i++Beispiel : Der Rückgabewert von in Ihrem Beispiel wäre tatsächlich 1, nicht 2 (der neue Wert von iwäre jedoch 2).

sepp2k
quelle
4
Mehr als jeder andere Grund bisher =scheint die Begründung, dass "alle Aufgaben einen in sich haben", sinnvoll zu sein. Ich kann das als ein starkes Festhalten an der Konsistenz respektieren.
Andy_Vulhop
was ist damit: a.capitalize! (implizite Zuordnung von a)
Luís Soares
1
@ LuísSoares a.capitalize!wird nicht neu zugewiesen a, sondern die Zeichenfolge, aauf die verwiesen wird , wird mutiert . Andere Verweise auf dieselbe Zeichenfolge sind betroffen. Wenn Sie dies a.object_idvor und nach dem Aufruf von tun capitalize, erhalten Sie dasselbe Ergebnis (beides wäre nicht der Fall, wenn Sie dies a = a.capitalizestattdessen tun würden ).
sepp2k
1
@ LuísSoares Wie gesagt, wirkt a.capitalize!sich auf andere Verweise auf dieselbe Zeichenfolge aus. Das ist ein praktischer Unterschied. Zum Beispiel, wenn Sie haben def yell_at(name) name.capitalize!; puts "HEY, #{name}!" endund Sie es dann so nennen : my_name = "luis"; yell_at(my_name), wird der Wert von my_namejetzt sein "LUIS", während es nicht beeinflusst würde, wenn Sie capitalizeund eine Zuordnung verwendet hätten.
sepp2k
1
Beeindruckend. Das ist beängstigend ... Zu wissen, dass Strings in Java unveränderlich sind. Aber mit der Macht geht Verantwortung einher. Danke für die Erklärung.
Luís Soares
25

Es ist nicht konventionell in OO-Sprachen. Tatsächlich gibt es ++in Smalltalk keine Sprache, die den Begriff "objektorientierte Programmierung" geprägt hat (und die Sprache, von der Ruby am stärksten beeinflusst wird). Was Sie damit meinen, ist, dass es in C konventionell ist und Sprachen C stark imitieren. Ruby hat eine etwas C-ähnliche Syntax, aber es ist nicht sklavisch, wenn man sich an C-Traditionen hält.

Warum es nicht in Ruby ist: Matz wollte es nicht. Das ist wirklich der ultimative Grund.

Der Grund , so etwas in Smalltalk vorhanden ist , weil es Teil der Sprache der Philosophie überschreibt , dass ein variable Zuordnung ist im Grunde eine andere Art von Sache , als eine Nachricht an ein Objekt zu senden - es auf einer anderen Ebene ist. Dieses Denken hat Matz wahrscheinlich bei der Gestaltung von Ruby beeinflusst.

Es wäre nicht unmöglich, es in Ruby aufzunehmen - Sie könnten leicht einen Präprozessor schreiben, der alles ++in verwandelt +=1. aber offensichtlich mochte Matz die Idee eines Operators, der eine "versteckte Aufgabe" ausführte, nicht. Es scheint auch ein wenig seltsam, einen Operator mit einem versteckten ganzzahligen Operanden darin zu haben. Kein anderer Operator in der Sprache funktioniert so.

Futter
quelle
1
Ich glaube nicht, dass Ihr Präprozessor-Vorschlag funktionieren würde. (kein Experte), aber ich denke, dass i = 42, i ++ 42 zurückgeben wird, wobei i + = 1 43 zurückgeben würde. Bin ich darin falsch? Ihr Vorschlag in diesem Fall wäre also, i ++ zu verwenden, da ++ i normalerweise verwendet wird, was imho ziemlich schlecht ist und mehr Schaden als Nutzen verursachen kann.
AturSams
12

Ich denke, es gibt noch einen anderen Grund: ++In Ruby wäre es nicht im entferntesten nützlich wie in C und seinen direkten Nachfolgern.

Der Grund dafür ist das forSchlüsselwort: Während es in C wichtig ist, ist es in Ruby meistens überflüssig. Der größte Teil der Iteration in Ruby erfolgt mit Enumerable-Methoden, z. B. eachund mapbeim Durchlaufen einer Datenstruktur und einer Fixnum#timesMethode, wenn Sie eine genaue Anzahl von Schleifen durchführen müssen.

Soweit ich gesehen habe, wird die meiste Zeit +=1von Leuten verwendet, die frisch aus C-Sprachen nach Ruby migriert sind.

Kurz gesagt, es ist wirklich fraglich, ob Methoden ++und --überhaupt verwendet werden würden.

Mladen Jablanović
quelle
1
Dies ist die beste Antwort imho. ++ wird häufig für die Iteration verwendet. Ruby empfiehlt diese Art der Iteration nicht.
AturSams
3

Ich denke, Matz argumentiert, dass er die Variable tatsächlich durch eine neue ersetzt.

Ex:

a = SomeClass.new
def a.go
  'Hallo'
Ende
# An dieser Stelle können Sie a.go anrufen
# aber wenn du ein a ++ gemacht hast
# das bedeutet wirklich a = a + 1
#, damit Sie a.go nicht mehr anrufen können
# wie Sie Ihr Original verloren haben

Wenn ihn jemand davon überzeugen könnte, dass er einfach #succ anrufen sollte! oder was nicht, das wäre sinnvoller und würde das Problem vermeiden. Sie können es auf Rubinkern vorschlagen.

Rogerdpack
quelle
9
"Sie können es auf Ruby Core vorschlagen" ... Nachdem Sie die Argumente in allen anderen Threads gelesen und verstanden haben, in denen es das letzte Mal vorgeschlagen wurde, und die Zeit davor und die Zeit davor und die Zeit davor, und die Zeit davor und ... Ich war noch nicht lange in der Ruby-Community, aber gerade während meiner Zeit erinnere ich mich an mindestens zwanzig solcher Diskussionen.
Jörg W Mittag
3

Sie können einen .+Selbstinkrementierungsoperator definieren :

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

Weitere Informationen zu "Klassenvariable" finden Sie unter " Klassenvariable zum Inkrementieren von Fixnum-Objekten ".

Sony Santos
quelle
2

Und in den Worten von David Black aus seinem Buch "The Well-Grounded Rubyist":

Einige Objekte in Ruby werden als unmittelbare Werte in Variablen gespeichert. Dazu gehören Ganzzahlen, Symbole (die wie folgt aussehen) und die speziellen Objekte true, false und nil. Wenn Sie einer Variablen einen dieser Werte zuweisen (x = 1), enthält die Variable den Wert selbst und keinen Verweis darauf. In der Praxis spielt dies keine Rolle (und es wird oft als impliziert belassen, anstatt wiederholt in Diskussionen über Referenzen und verwandte Themen in diesem Buch dargelegt zu werden). Ruby übernimmt die Dereferenzierung von Objektreferenzen automatisch. Sie müssen keine zusätzliche Arbeit leisten, um eine Nachricht an ein Objekt zu senden, das beispielsweise einen Verweis auf eine Zeichenfolge enthält, im Gegensatz zu einem Objekt, das einen unmittelbaren ganzzahligen Wert enthält. Die Sofortwert-Repräsentationsregel hat jedoch einige interessante Konsequenzen: besonders wenn es um ganze Zahlen geht. Zum einen ist jedes Objekt, das als unmittelbarer Wert dargestellt wird, immer genau dasselbe Objekt, unabhängig davon, wie vielen Variablen es zugewiesen ist. Es gibt nur ein Objekt 100, nur ein Objekt falsch und so weiter. Die unmittelbare, einzigartige Natur von ganzzahligen Variablen ist der Grund dafür, dass Ruby keine Operatoren vor und nach dem Inkrementieren hat - das heißt, Sie können dies in Ruby nicht tun: x = 1 x ++ # Kein solcher Operator Der Grund dafür ist der Grund Für das unmittelbare Vorhandensein von 1 in x wäre x ++ wie 1 ++, was bedeutet, dass Sie die Nummer 1 in die Nummer 2 ändern würden - und das macht keinen Sinn. egal wie vielen Variablen es zugeordnet ist. Es gibt nur ein Objekt 100, nur ein Objekt falsch und so weiter. Die unmittelbare, einzigartige Natur von ganzzahligen Variablen ist der Grund dafür, dass Ruby keine Operatoren vor und nach dem Inkrementieren hat - das heißt, Sie können dies in Ruby nicht tun: x = 1 x ++ # Kein solcher Operator Der Grund dafür ist der Grund Für das unmittelbare Vorhandensein von 1 in x wäre x ++ wie 1 ++, was bedeutet, dass Sie die Nummer 1 in die Nummer 2 ändern würden - und das macht keinen Sinn. egal wie vielen Variablen es zugeordnet ist. Es gibt nur ein Objekt 100, nur ein Objekt falsch und so weiter. Die unmittelbare, einzigartige Natur von ganzzahligen Variablen ist der Grund dafür, dass Ruby keine Operatoren vor und nach dem Inkrementieren hat - das heißt, Sie können dies in Ruby nicht tun: x = 1 x ++ # Kein solcher Operator Der Grund dafür ist der Grund Für das unmittelbare Vorhandensein von 1 in x wäre x ++ wie 1 ++, was bedeutet, dass Sie die Nummer 1 in die Nummer 2 ändern würden - und das macht keinen Sinn.

Alexander Swann
quelle
Aber warum kannst du dann "1.next" machen?
Magne
1

Könnte dies nicht erreicht werden, indem der Klasse fixnum oder Integer eine neue Methode hinzugefügt wird?

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

gibt 2 zurück

"Destruktive" Methoden scheinen angehängt zu sein !, um mögliche Benutzer zu warnen. Das Hinzufügen einer neuen Methode namens next!würde also so ziemlich das tun, was angefordert wurde, d. H.

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

gibt 2 zurück (da numb erhöht wurde)

Natürlich next!müsste die Methode überprüfen, ob das Objekt eine ganzzahlige Variable und keine reelle Zahl ist, aber dies sollte verfügbar sein.

Sjerek
quelle
1
Integer#nextexistiert bereits (mehr oder weniger), außer es wird Integer#succstattdessen aufgerufen (für 'Nachfolger'). Aber Integer#next!(oder Integer#succ!) wäre Unsinn: Denken Sie daran, dass Methoden an Objekten arbeiten , nicht an Variablen . Sie numb.next!wären also genau gleich 1.next!, dh sie würden 1 mutieren, um gleich 2 zu sein . ++wäre etwas besser, da es syntaktischer Zucker für eine Aufgabe sein könnte, aber ich persönlich bevorzuge die aktuelle Syntax, bei der alle Aufgaben erledigt sind =.
Philomory
Um den obigen Kommentar zu vervollständigen: und Integer#predum den Vorgänger abzurufen.
Yoni
-6

Überprüfen Sie diese Operatoren aus der C-Familie in Ruby's irb und testen Sie sie selbst:

x = 2    # x is 2
x += 2   # x is 4
x++      # x is now 8
++x      # x reverse to 4
Aung Zan Baw
quelle
3
Dies ist eindeutig falsch und funktioniert nicht, ebenso (x++)wie eine ungültige Aussage in Ruby.
Anothermh