2010-09-07 17 views
116

El operador de incremento/decremento pre/post (++ y --) es una sintaxis del lenguaje de programación bastante estándar (para lenguajes de procedimiento y orientados a objetos, al menos).¿Por qué Ruby no admite i ++ o i-- (operadores de incremento/disminución)?

¿Por qué Ruby no los admite? Entiendo que podría lograr lo mismo con += y -=, pero parece extrañamente arbitrario excluir algo así, especialmente porque es muy conciso y convencional.

Ejemplo:

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 

entiendo Fixnum es inmutable, pero si += solo puede instanciar un nuevo Fixnum y configurarlo, por qué no hacer lo mismo para ++?

¿La consistencia en las asignaciones que contienen el carácter = es la única razón para esto, o me falta algo?

+2

código fuente Grep ruby ​​para tales operadores. Si no hay ninguno, a Matz no le gustan. – Eimantas

+0

No puede hacer un preincremento con un operador '+ ='. En C intento usar '++'/'--' solo dentro de los condicionales, prefiriendo para el más literal' + = '/' - = 'en una declaración básica. Probablemente porque aprendí Python (mucho después de C ...) –

+0

¿No hubo una pregunta como esta para Python ayer? – BoltClock

Respuesta

82

Aquí es cómo Matz (Yukihiro Matsumoto) lo explica en una antigua 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. 
+8

2 y 3 parecen contradictorios. Si la autoasignación es mala, ¿por qué son '+ ='/'- =' ok? ¿Y no sería '1 + = 1' tan malo? (No funciona en IRB con 'error de sintaxis, ASIGNACIÓN inesperada ') –

+2

(2) significa que en C, no estás alterando el valor en sí ... estás alterando el contenido de la variable que contiene el valor. Eso es demasiado meta para cualquier lenguaje que pase por valor.A menos que haya una manera de pasar algo por referencia en Ruby (y me refiero a "referencia", sin pasar una referencia por valor), no sería posible modificar la variable en sí misma dentro de un método. – cHao

+4

Tal vez me esté perdiendo algo aquí. '+ =' reemplaza el objeto al que la variable hace referencia con un objeto completamente nuevo. Puede verificar esto llamando a 'i.object_id' antes y después de' i + = 1'. ¿Por qué sería más complicado hacerlo con '++'? –

27

Una razón es que hasta ahora cada operador de asignación (es decir, un operador que cambia una variable) tiene un = en él. Si agrega ++ y --, ese ya no es el caso.

Otra razón es que el comportamiento de ++ y -- a menudo confunde a las personas. Ejemplo: el valor de retorno de i++ en su ejemplo sería en realidad 1, no 2 (sin embargo, el nuevo valor de i sería 2).

+4

Más que cualquier otra razón hasta ahora, la lógica de que "todas las asignaciones tienen un' = 'en ellas" parece tener sentido. De alguna manera puedo respetar eso como una adherencia feroz a la coherencia. –

+0

qué tal esto: a.capitalize! (asignación implícita de a) –

+1

@ LuísSoares 'a.capitalize!' No reasigna 'a', mutará la cadena a la que se refiere' a'. Otras referencias a la misma cadena se verán afectadas y si haces 'a.object_id' antes y después de la llamada a' capitalizar', obtendrás el mismo resultado (ninguno de los dos sería verdadero si hicieras 'a = a. capitalize' en su lugar). – sepp2k

3

creo que el razonamiento Matz' para no tener gusto de ellos es que en realidad sustituye a la variable con una nueva.

ejemplo:

 
a = SomeClass.new 
def a.go 
    'hello' 
end 
# at this point, you can call a.go 
# but if you did an a++ 
# that really means a = a + 1 
# so you can no longer call a.go 
# as you have lost your original 

Ahora bien, si alguien le podía convencer de que sólo debe llamar #succ! o qué no, eso tendría más sentido y evitaría el problema. Puedes sugerirlo en ruby ​​core.

+9

"Puedes sugerirlo en ruby ​​core" ... * Después * has leído * y * has entendido los argumentos en todos los otros hilos donde se sugirieron la última vez, la anterior y la anterior, y el tiempo antes de eso, y el tiempo anterior a eso, y ... No he estado en la comunidad de Ruby por mucho tiempo, pero justo durante mi tiempo, recuerdo al menos veinte de tales discusiones. –

23

No es convencional en los lenguajes OO. De hecho, no hay ++ en Smalltalk, el lenguaje que acuñó el término "programación orientada a objetos" (y el lenguaje que más influencia tiene Ruby). Lo que quiere decir es que es convencional en C y los lenguajes que imitan estrechamente a C. Ruby tiene una sintaxis similar a C, pero no es servil al adherirse a las tradiciones C.

En cuanto a por qué no está en Ruby: Matz no lo quería. Esa es realmente la razón última.

La razón no existe tal cosa en Smalltalk es porque es parte de la filosofía primordial del lenguaje que la asignación de una variable es fundamentalmente un tipo diferente de cosas que el envío de un mensaje a un objeto - está en un nivel diferente. Este pensamiento probablemente influenció a Matz en el diseño de Ruby.

No sería imposible incluirlo en Ruby: podría escribir fácilmente un preprocesador que transforme todos ++ en +=1.pero evidentemente a Matz no le gustaba la idea de un operador que hiciera una "tarea oculta". También parece un poco extraño tener un operador con un operando entero oculto dentro de él. Ningún otro operador en el lenguaje funciona de esa manera.

+1

No creo que la sugerencia del preprocesador funcione; (no es un experto) pero creo que i = 42, i ++ devolverá 42 donde i + = 1 devolvería 43. ¿Soy incorrecto en esto? Entonces, su sugerencia en ese caso sería usar i ++ ya que normalmente se utiliza ++ i, lo cual es bastante malo y puede causar más daño que beneficio. – zehelvion

8

Creo que hay otra razón: ++ en Ruby no sería remotamente útil como en C y sus sucesores directos.

La razón es que la palabra clave for: si bien es esencial en C, en su mayor parte es superfluo en Ruby. La mayor parte de la iteración en Ruby se realiza a través de métodos Enumerable, como each y map al iterar a través de alguna estructura de datos, y el método Fixnum#times, cuando necesita repetir un número exacto de veces.

En realidad, por lo que he visto, la mayoría de las veces +=1 es utilizado por personas recién migradas a Ruby desde lenguajes de estilo C.

En resumen, es realmente cuestionable si se usan los métodos ++ y --.

+1

Esta es la mejor respuesta. ++ a menudo se usa para iteración. Ruby no fomenta este tipo de iteración. – zehelvion

2

puede definir un operador .+ auto-subasta:

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 

Más información sobre la "clase variable" está disponible en "Class Variable to increment Fixnum objects".

1

¿No se pudo lograr esto agregando un nuevo método a la clase Fixnum o Integer?

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

retornos 2

métodos "destructivos" parecen ser añadido con ! para advertir a los posibles usuarios, por lo que añadir un nuevo método llamado next! sería más o menos hacer lo que se pide es decir.

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

retornos 2 (desde entumecido ha sido incrementado)

Por supuesto, el método next! tendría que comprobar que el objeto era una variable entera y no es un número real, pero esto debe estar disponibles .

+1

'Número entero 'next' ya existe (más o menos), excepto que se llama' Integer # succ' en su lugar (para' successor '). ¡Pero 'Integer # next!' (O 'Integer # succ!') No tendría sentido: recuerde que los métodos funcionan en _objects_, not _variables_, así que 'numb.next!' Sería exactamente igual a '1.next!', Que es decir, mutaría 1 para ser igual a 2_. '++' sería marginalmente mejor, ya que podría ser azúcar sintáctica para una tarea, pero personalmente prefiero la sintaxis actual donde todas las asignaciones se hacen con '='. – philomory

-3

Comprobar estos operadores de la C-familia en el IRB de Ruby y probarlos por sí mismo:

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

Esto está claramente mal y no funciona, ya que '(x ++)' es una declaración inválida en Ruby. – anothermh

2

Y en las palabras de David Negro de su libro "El Rubyist a tierra bien":

Algunos objetos en Ruby se almacenan en variables como valores inmediatos. Estos incluyen números enteros , símbolos (que se parecen a esto) y los objetos especiales verdadero, falso y nil. Cuando asigna uno de estos valores a una variable (x = 1), la variable contiene el valor mismo, en lugar de una referencia a él. En términos prácticos, esto no importa (y a menudo se dejará como implícito, en lugar de explicado repetidamente, en discusiones de referencias y temas relacionados en este libro). Ruby maneja la desreferencia de referencias de objeto automáticamente; no es necesario que haga ningún trabajo adicional para enviar un mensaje a un objeto que contiene, por ejemplo, una referencia a una cadena, a diferencia de un objeto que contiene un valor entero inmediato. Pero la regla de representación de valor inmediato tiene un par de ramificaciones interesantes, especialmente cuando se trata de enteros. Por un lado, cualquier objeto que se represente como un valor inmediato siempre es exactamente el mismo objeto, sin importar cuántas variables se haya asignado. Solo hay un objeto 100, solo un objeto falso, y , etc. El inmediata naturaleza, única de las variables enteras de ruedas está detrás de la falta de operadores -que de incremento previo y posterior, es decir de Ruby, no se puede hacer esto en Rubí: x = 1 x ++ # No existe el operador La razón es que debido a la presencia inmediata de 1 en x, x ++ sería como 1 ++, , lo que significa que estaría cambiando el número 1 por el número 2, y eso hace que no tenga sentido.

+0

Pero, ¿cómo es que puedes hacer "1.next", entonces? – Magne

Cuestiones relacionadas