web-dev-qa-db-fra.com

Pourquoi Ruby ne prend-il pas en charge i ++ ou i-- (opérateurs d'incrémentation / décrémentation)?

L'opérateur pré/post incrémentation/décrémentation (++ et --) sont une syntaxe de langage de programmation assez standard (pour les langages procéduraux et orientés objet, au moins).

Pourquoi Ruby ne les prend-il pas en charge? Je comprends que vous pourriez accomplir la même chose avec += et -=, mais il semble étrangement arbitraire d'exclure quelque chose comme ça, d'autant plus que c'est tellement concis et conventionnel.

Exemple:

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

Je comprends que Fixnum est immuable, mais si += peut simplement instancier un nouveau Fixnum et le définir, pourquoi ne pas faire de même pour ++?

La cohérence des affectations contenant le = le caractère est la seule raison à cela, ou est-ce que je manque quelque chose?

127
Andy_Vulhop

Voici comment Matz (Yukihiro Matsumoto) l'explique dans un vieux 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.
90
Brian

L'une des raisons est que jusqu'à présent, chaque opérateur d'affectation (c'est-à-dire un opérateur qui modifie une variable) a un = dedans. Si vous ajoutez ++ et --, ce n'est plus le cas.

Une autre raison est que le comportement de ++ et -- confondent souvent les gens. Exemple: la valeur de retour de i++ dans votre exemple serait en fait 1, pas 2 (la nouvelle valeur de i serait cependant 2).

28
sepp2k

Ce n'est pas conventionnel dans les langues OO. En fait, il n'y a pas de ++ dans Smalltalk, le langage qui a inventé le terme "programmation orientée objet" (et le langage Ruby est le plus fortement influencé par). Ce que vous voulez dire, c'est qu'il est conventionnel en C et les langages imitant étroitement C. Ruby a une syntaxe quelque peu semblable à C, mais il n'est pas servile en adhérant aux traditions C .

Quant à savoir pourquoi ce n'est pas dans Ruby: Matz n'en voulait pas. C'est vraiment la raison ultime.

La raison pour laquelle une telle chose n'existe pas dans Smalltalk est parce que cela fait partie de la philosophie dominante du langage que l'attribution d'une variable est fondamentalement un type de chose différent que d'envoyer un message à un objet - c'est à un niveau différent. Cette pensée a probablement influencé Matz dans la conception de Ruby.

Il ne serait pas impossible de l'inclure dans Ruby - vous pourriez facilement écrire un préprocesseur qui transforme tous les ++ en +=1. mais de toute évidence, Matz n'aimait pas l'idée d'un opérateur qui effectuait une "affectation cachée". Il semble également un peu étrange d'avoir un opérateur avec un opérande entier caché à l'intérieur. Aucun autre opérateur dans la langue ne fonctionne de cette façon.

23
Chuck

Je pense qu'il y a une autre raison: ++ in Ruby ne serait pas utile à distance comme dans C et ses successeurs directs.

La raison étant, le mot clé for: bien qu'il soit essentiel en C, il est surtout superflu en Ruby. La plupart de l'itération dans Ruby se fait via des méthodes Enumerable, telles que each et map lors de l'itération à travers une certaine structure de données, et Fixnum#times, lorsque vous devez effectuer une boucle un nombre exact de fois.

En fait, pour autant que je sache, la plupart du temps +=1 est utilisé par les personnes récemment migrées vers Ruby à partir des langages de style C.).

En bref, il est vraiment discutable si les méthodes ++ et -- serait utilisé du tout.

11
Mladen Jablanović

Je pense que le raisonnement de Matz pour ne pas les aimer est qu'il remplace en fait la variable par une nouvelle.

ex:

 a = SomeClass.new 
 def a.go 
 'bonjour' 
 fin 
 # à ce stade, vous pouvez appeler a.go 
 # mais si vous avez fait un a ++ 
 # cela signifie vraiment a = a + 1 
 # donc vous ne pouvez plus appeler a.go 
 # car vous avez perdu votre original 

Maintenant, si quelqu'un pouvait le convaincre qu'il devrait simplement appeler #succ! ou non, cela aurait plus de sens et éviterait le problème. Vous pouvez le suggérer sur Ruby core.

3
rogerdpack

Vous pouvez définir un .+ opérateur d'auto-incrémentation:

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

Plus d'informations sur "Variable de classe" sont disponibles dans " Variable de classe pour incrémenter les objets Fixnum ".

3
Sony Santos

Et selon les mots de David Black dans son livre "The Well-Grounded Rubyist":

Certains objets dans Ruby sont stockés dans des variables en tant que valeurs immédiates. Il s'agit notamment des entiers, des symboles (qui ressemblent à ceci) et des objets spéciaux true, false et nil. Lorsque vous affectez l'un des ces valeurs à une variable (x = 1), la variable contient la valeur elle-même, plutôt qu'une référence à celle-ci. En pratique, cela n'a pas d'importance (et elle sera souvent laissée comme implicite, plutôt qu'expliquée à plusieurs reprises, dans les discussions sur les références et les sujets connexes dans ce livre). Ruby gère automatiquement le déréférencement des références d'objets; vous n'avez pas à faire de travail supplémentaire pour envoyer un message à un objet qui contient, par exemple, une référence à une chaîne, par opposition à un objet qui contient une valeur entière immédiate. Mais la règle de représentation de la valeur immédiate a quelques ramifications intéressantes, en particulier en ce qui concerne les nombres entiers. D'une part, tout objet représenté comme une valeur immédiate est toujours exactement le même objet, quel que soit le nombre de variables i t est affecté à. Il n'y a qu'un seul objet 100, un seul objet faux, etc. La nature immédiate et unique des variables liées à des entiers est à l'origine du manque d'opérateurs de pré et post-incrémentation de Ruby, ce qui signifie que vous ne pouvez pas le faire dans Ruby: x = 1 x ++ # Aucun opérateur de ce type La raison en est que à la présence immédiate de 1 dans x, x ++ serait comme 1 ++, ce qui signifie que vous changeriez le nombre 1 en nombre 2 - et cela n'a aucun sens.

2
Alexander Swann

Cela ne pourrait-il pas être réalisé en ajoutant une nouvelle méthode à la classe fixnum ou Integer?

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

renvoie 2

Les méthodes "destructives" semblent être ajoutées à ! pour avertir les utilisateurs potentiels, en ajoutant une nouvelle méthode appelée next! ferait à peu près ce qui était demandé, c'est-à-dire.

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

renvoie 2 (puisque numb a été incrémenté)

Bien sûr, le next! la méthode devrait vérifier que l'objet était une variable entière et non un nombre réel, mais cela devrait être disponible.

1
Sjerek