Por que o Ruby não suporta i ++ ou i (operadores de incremento / decremento)?

O operador de incremento / decremento pré / pós ( ++ e -- ) é uma syntax de linguagem de programação padrão (para linguagens procedurais e orientadas a objects, pelo menos).

Por que o Ruby não suporta eles? Eu entendo que você poderia realizar a mesma coisa com += e -= , mas parece estranhamente arbitrário excluir algo assim, especialmente porque é tão conciso e convencional.

Exemplo:

 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 

Eu entendo Fixnum é imutável, mas se += pode apenas instanciar um novo Fixnum e defini-lo, porque não fazer o mesmo para ++ ?

A consistência nas atribuições contendo o caractere = a única razão para isso, ou estou faltando alguma coisa?

Aqui está como Matz (Yukihiro Matsumoto) explica isso em um tópico antigo:

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

Uma razão é que, até agora, todo operador de atribuição (ou seja, um operador que altera uma variável) tem um = nele. Se você adicionar ++ e -- , isso não é mais o caso.

Outra razão é que o comportamento de ++ e -- muitas vezes confunde as pessoas. Caso em questão: O valor de retorno de i++ no seu exemplo seria realmente 1, não 2 (o novo valor de i seria 2, no entanto).

Não é convencional em idiomas OO. Na verdade, não há nenhum ++ em Smalltalk, a linguagem que cunhou o termo “programação orientada a objects” (e a linguagem pela qual Ruby é mais fortemente influenciado). O que você quer dizer é que é convencional em C e línguas imitando de perto C. Ruby tem uma syntax semelhante a C, mas não é servil em aderir às tradições C.

Por que não está em Ruby: Matz não queria. Esse é realmente o motivo final.

A razão pela qual isso não existe em Smalltalk é porque é parte da filosofia dominante da linguagem que atribuir uma variável é fundamentalmente um tipo diferente de coisa do que enviar uma mensagem para um object – está em um nível diferente. Esse pensamento provavelmente influenciou o Matz na criação do Ruby.

Não seria impossível incluí-lo em Ruby – você poderia facilmente escrever um pré-processador que transforma todo o ++ em +=1 . mas evidentemente Matz não gostou da ideia de um operador que fez uma “tarefa oculta”. Também parece um pouco estranho ter um operador com um operando inteiro oculto dentro dele. Nenhum outro operador no idioma funciona dessa maneira.

Eu acho que há outra razão: ++ em Ruby não seria remotamente útil como em C e seus sucessores diretos.

A razão de ser, a palavra-chave for : enquanto é essencial em C, é principalmente supérflua em Ruby. A maior parte da iteração em Ruby é feita através de methods Enumerable, como each e map quando iterando através de alguma estrutura de dados, e método Fixnum#times , quando você precisa fazer um loop num número exato de vezes.

Na verdade, até onde eu vi, a maior parte do tempo +=1 é usada por pessoas recém-migradas para o Ruby de linguagens do estilo C.

Em suma, é realmente questionável se os methods ++ e -- seriam usados.

Acho que o raciocínio de Matz por não gostar deles é que, na verdade, substitui a variável por uma nova.

ex:

 a = SomeClass.new
 def a.go
   'Olá'
 fim
 # neste ponto, você pode ligar para a.go
 # mas se você fez um ++
 # isso realmente significa a = a + 1
 # para que você não possa mais chamar a.go
 # como você perdeu seu original

Agora, se alguém pudesse convencê-lo de que deveria apenas chamar #succ! ou o que não, isso faria mais sentido e evitaria o problema. Você pode sugerir no núcleo de ruby.

Você pode definir um operador .+ Auto-incremento:

 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 

Mais informações sobre “class Variable” estão disponíveis em ” Class Variable para incrementar objects Fixnum “.

E nas palavras de David Black de seu livro “The Well-Grounded Rubyist”:

Alguns objects no Ruby são armazenados em variables ​​como valores imediatos. Estes incluem números inteiros, símbolos (que se parecem com isto), e os objects especiais true, false e nil. Quando você atribui um desses valores a uma variável (x = 1), a variável mantém o valor em si, em vez de uma referência a ele. Em termos práticos, isso não importa (e muitas vezes será deixado como implícito, em vez de explicitado repetidamente, em discussões de referências e tópicos relacionados neste livro). Ruby manipula a desreferência de referências de objects automaticamente; você não precisa fazer nenhum trabalho extra para enviar uma mensagem a um object que contenha, digamos, uma referência a uma string, em oposição a um object que contenha um valor inteiro imediato. Mas a regra de representação de valor imediato tem algumas ramificações interessantes, especialmente quando se trata de números inteiros. Por um lado, qualquer object que é representado como um valor imediato é sempre exatamente o mesmo object, não importa quantas variables ​​sejam atribuídas. Há apenas um object 100, apenas um object falso e assim por diante. A natureza imediata e única das variables ​​ligadas a inteiros está por trás da falta de operadores de pré e pós-incremento de Ruby – o que significa que você não pode fazer isso em Ruby: x = 1 x ++ para a presença imediata de 1 em x, x ++ seria como 1 ++, o que significa que você estaria mudando o número 1 para o número 2 – e isso não faz sentido.

Isso não poderia ser alcançado adicionando um novo método à class fixnum ou Integer?

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

devolve 2

Métodos “destrutivos” parecem ser anexados com ! para avisar possíveis usuários, então adicione um novo método chamado next! faria praticamente o que foi solicitado ou seja.

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

retorna 2 (desde que o numb foi incrementado)

Claro, o next! O método teria que verificar se o object era uma variável inteira e não um número real, mas isso deveria estar disponível.

Verifique estes operadores da família C no irb do Ruby e teste-os por si próprio:

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