Perché Ruby non supporta i ++ o i-- (operatori di incremento / decremento)?


130

Gli operatori pre / post incremento / decremento ( ++e --) sono sintassi del linguaggio di programmazione piuttosto standard (almeno per linguaggi procedurali e orientati agli oggetti).

Perché Ruby non li supporta? Capisco che potresti realizzare la stessa cosa con +=e -=, ma sembra stranamente arbitrario escludere qualcosa del genere, soprattutto perché è così conciso e convenzionale.

Esempio:

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

Capisco che Fixnumè immutabile, ma se è +=possibile solo istanziare un nuovo Fixnume impostarlo, perché non fare lo stesso per ++?

La coerenza negli incarichi che contengono il =personaggio è l'unica ragione di ciò o mi sto perdendo qualcosa?


2
Codice sorgente ruby ​​grep per tali operatori. Se non ce ne sono, a Matz non piacciono.
Eimantas,

Non puoi fare preincrementi con un +=operatore. In CI prova a usare ++/ --solo all'interno dei condizionali, preferendo il più letterale +=/ -=in un'istruzione di base. Probabilmente perché ho imparato Python (molto tempo dopo C ...)
Nick T

Non c'era una domanda come questa per Python proprio ieri?
BoltClock

@Eimantas, ovviamente, non sono piaciuti ai creatori della lingua. È troppo comune per trascurare. Mi chiedevo PERCHÉ, che è stato in qualche modo chiarito dalle risposte di seguito.
Andy_Vulhop,

1
penso che questa sia (quasi) una domanda SO modello. Non è qualcosa che non sia facilmente googleable per ottenere una risposta ponderata. È abbastanza chiaro e specifico in quale risposta è richiesta e la risposta fa luce su un aspetto della programmazione che può far pensare in modo più ampio rispetto al nocciolo della domanda.
PurplePilot,

Risposte:


97

Ecco come Matz (Yukihiro Matsumoto) lo spiega in un vecchio thread :

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 e 3 sembrano contraddittori. Se l'autoassegnazione non va bene, perché +=/ -=ok? E non 1+=1sarebbe altrettanto male? (Fallisce in IRB con syntax error, unexpected ASSIGNMENT)
Andy_Vulhop

2
(2) significa che in C non stai modificando il valore stesso ... stai alterando il contenuto della variabile che contiene il valore. È un po 'troppo meta per qualsiasi lingua che passa per valore. A meno che non ci sia un modo per passare qualcosa per riferimento in Ruby (e intendo veramente "per riferimento", non passare un riferimento per valore), la modifica della variabile stessa non sarebbe possibile all'interno di un metodo.
cHao,

5
Forse mi manca qualcosa qui. +=sostituisce l'oggetto a cui fa riferimento la variabile con un oggetto completamente nuovo. Puoi verificarlo chiamando i.object_idprima e dopo i+=1. Perché sarebbe più tecnicamente complicato da fare ++?
Andy_Vulhop,

6
@Andy_Vulhop: # 3 sta spiegando perché è tecnicamente impossibile che l'assegnazione sia un metodo, non perché l'assegnazione sia impossibile in generale (il poster Matz stava rispondendo pensando che fosse possibile creare un ++metodo).
Chuck,

2
In Ruby tutti i letterali sono anche oggetti. Quindi credo che Matz stia cercando di dire che non è sicuro che gli piaccia l'idea di trattare 1 ++ come una dichiarazione. Personalmente penso che sia irragionevole dato che @Andy_Vulhop dice che 1 + = 2 è altrettanto pazzo, e Ruby solleva un errore quando lo fai. Quindi 1 ++ non è più difficile da gestire. Forse la necessità del parser di far fronte a quel tipo di zucchero sintattico è indesiderabile.
Steve Midgley,

28

Uno dei motivi è che fino ad ora ogni operatore di assegnazione (ovvero un operatore che cambia una variabile) ha un valore =al suo interno. Se aggiungi ++e-- , non è più così.

Un altro motivo è che il comportamento ++e --spesso confonde le persone. Caso in questione: il valore restituito di i++nel tuo esempio sarebbe effettivamente 1, non 2 (il nuovo valore di isarebbe 2, tuttavia).


4
Più di ogni altra ragione finora, il razionale che "tutti gli incarichi hanno un =in loro" sembra avere un senso. Posso in qualche modo rispettarlo come una feroce aderenza alla coerenza.
Andy_Vulhop,

che dire di questo: a.capitalize! (assegnazione implicita di a)
Luís Soares,

1
@ LuísSoares a.capitalize!non si riassegna a, cambierà la stringa a cui si ariferisce. Altri riferimenti alla stessa stringa saranno interessati e se lo fai a.object_idprima e dopo la chiamata capitalize, otterrai lo stesso risultato (nessuno dei quali sarebbe vero se a = a.capitalizeinvece lo facessi ).
sepp2k,

1
@ LuísSoares Come ho detto, a.capitalize!influenzerà altri riferimenti alla stessa stringa. Questa è davvero una differenza pratica. Ad esempio, se lo hai def yell_at(name) name.capitalize!; puts "HEY, #{name}!" ende lo chiami in questo modo:, my_name = "luis"; yell_at(my_name)il valore di my_namesarà ora "LUIS", mentre non sarebbe interessato se tu avessi usato capitalizee un incarico.
sepp2k,

1
Wow. È spaventoso ... Sapere che in Java le stringhe sono immutabili .. Ma con il potere deriva la responsabilità. Grazie per la spiegazione.
Luís Soares,

25

Non è convenzionale nelle lingue OO. In effetti, non esiste ++in Smalltalk, il linguaggio che ha coniato il termine "programmazione orientata agli oggetti" (e il linguaggio da cui Ruby è maggiormente influenzato). Quello che vuoi dire è che è convenzionale in C e nei linguaggi che imitano da vicino C. Ruby ha una sintassi un po 'simile a C, ma non è servile aderire alle tradizioni C.

Per quanto riguarda il motivo per cui non è in Ruby: Matz non lo voleva. Questa è davvero la ragione ultima.

Il motivo per cui nulla di simile esiste in Smalltalk è perché fa parte della filosofia prevalente del linguaggio che l'assegnazione di una variabile è fondamentalmente un tipo diverso di cosa rispetto all'invio di un messaggio a un oggetto - è a un livello diverso. Questo pensiero probabilmente influenzò Matz nel progettare Ruby.

Non sarebbe impossibile includerlo in Ruby: potresti facilmente scrivere un preprocessore che trasforma tutto ++in +=1. ma evidentemente a Matz non piaceva l'idea di un operatore che svolgesse un "incarico nascosto". Sembra anche un po 'strano avere un operatore con un intero operando nascosto al suo interno. Nessun altro operatore nella lingua funziona in questo modo.


1
Non penso che il suggerimento del preprocessore funzionerebbe; (non un esperto) ma penso che i = 42, i ++ restituirà 42 dove i + = 1 restituirà 43. Sono errato in questo? Quindi il tuo suggerimento in quel caso sarebbe di usare i ++ come ++ i che normalmente viene usato, il che è un pessimo imho e può causare più danni che benefici.
AturSams,

12

Penso che ci sia un altro motivo: ++in Ruby non sarebbe molto utile come in C e nei suoi successori diretti.

Il motivo è la forparola chiave: mentre è essenziale in C, è per lo più superfluo in Ruby. La maggior parte dell'iterazione in Ruby viene eseguita tramite metodi Enumerable, come eache mapquando si scorre attraverso una struttura di dati, eFixnum#times metodo, quando è necessario eseguire il ciclo di un numero esatto di volte.

In realtà, per quanto ho visto, il più delle volte +=1viene utilizzato da persone appena migrate su Ruby da linguaggi in stile C.

In breve, è davvero discutibile se i metodi ++e --sarebbero stati utilizzati affatto.


1
Questa è la migliore risposta imho. ++ è spesso usato per l'iterazione. Ruby non incoraggia questo tipo di iterazione.
AturSams,

3

Penso che il ragionamento di Matz per non gradirli sia che in realtà sostituisce la variabile con una nuova.

ex:

a = SomeClass.new
def a.go
  'Ciao'
fine
# a questo punto, puoi chiamare a.go
# ma se hai fatto un ++
# significa davvero a = a + 1
# quindi non puoi più chiamare a.go
# come hai perso l'originale

Ora, se qualcuno potesse convincerlo che dovrebbe semplicemente chiamare #succ! o cosa no, avrebbe più senso ed eviterebbe il problema. Puoi suggerirlo sul nucleo rubino.


9
"Puoi suggerirlo su ruby ​​core" ... Dopo aver letto e compreso gli argomenti in tutti gli altri thread in cui è stato suggerito l'ultima volta, e il tempo prima e il tempo prima e il tempo prima, e il tempo prima, e ... Non sono stato nella comunità di Ruby molto a lungo, ma proprio durante il mio tempo, ricordo almeno venti di tali discussioni.
Jörg W Mittag,

3

È possibile definire un .+operatore autoincremento:

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

Ulteriori informazioni su "Variabile di classe" sono disponibili in " Variabile di classe per incrementare gli oggetti Fixnum ".


2

E nelle parole di David Black dal suo libro "The Well-Grounded Rubyist":

Alcuni oggetti in Ruby sono memorizzati in variabili come valori immediati. Questi includono numeri interi, simboli (che assomigliano a: questo) e gli oggetti speciali true, false e nil. Quando assegni uno di questi valori a una variabile (x = 1), la variabile conserva il valore stesso, anziché un riferimento ad esso. In termini pratici, questo non ha importanza (e spesso verrà lasciato come implicito, anziché spiegato più volte, nelle discussioni su riferimenti e argomenti correlati in questo libro). Ruby gestisce automaticamente la dereferenziazione dei riferimenti agli oggetti; non devi fare alcun lavoro extra per inviare un messaggio a un oggetto che contiene, per esempio, un riferimento a una stringa, al contrario di un oggetto che contiene un valore intero immediato. Ma la regola di rappresentazione del valore immediato ha un paio di ramificazioni interessanti, soprattutto quando si tratta di numeri interi. Per prima cosa, qualsiasi oggetto rappresentato come valore immediato è sempre esattamente lo stesso oggetto, indipendentemente dal numero di variabili a cui è assegnato. C'è solo un oggetto 100, solo un oggetto falso e così via. La natura unica e immediata delle variabili associate a numeri interi è alla base della mancanza di operatori pre e post-incremento di Ruby, vale a dire che non è possibile farlo in Ruby: x = 1 x ++ # Nessun operatore di questo tipo Il motivo è che per la presenza immediata di 1 in x, x ++ sarebbe come 1 ++, il che significa che cambieresti il ​​numero 1 in numero 2, e questo non ha senso. non importa a quante variabili è assegnato. C'è solo un oggetto 100, solo un oggetto falso e così via. La natura unica e immediata delle variabili associate a numeri interi è alla base della mancanza di operatori pre e post-incremento di Ruby, vale a dire che non è possibile farlo in Ruby: x = 1 x ++ # Nessun operatore di questo tipo Il motivo è che per la presenza immediata di 1 in x, x ++ sarebbe come 1 ++, il che significa che cambieresti il ​​numero 1 in numero 2, e questo non ha senso. non importa a quante variabili è assegnato. C'è solo un oggetto 100, solo un oggetto falso e così via. La natura unica e immediata delle variabili associate a numeri interi è alla base della mancanza di operatori pre e post-incremento di Ruby, vale a dire che non è possibile farlo in Ruby: x = 1 x ++ # Nessun operatore di questo tipo Il motivo è che per la presenza immediata di 1 in x, x ++ sarebbe come 1 ++, il che significa che cambieresti il ​​numero 1 in numero 2, e questo non ha senso.


Ma come mai puoi fare "1.next" allora?
Magne,

1

Non è possibile raggiungere questo obiettivo aggiungendo un nuovo metodo alla classe fixnum o Integer?

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

restituisce 2

I metodi "distruttivi" sembrano essere aggiunti !per avvisare i possibili utenti, quindi l'aggiunta di un nuovo metodo chiamato next!farebbe praticamente ciò che è stato richiesto, ad es.

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

ritorna 2 (da quando il numb è stato incrementato)

Ovviamente, il next!metodo dovrebbe verificare che l'oggetto fosse una variabile intera e non un numero reale, ma questo dovrebbe essere disponibile.


1
Integer#nextesiste già (più o meno), tranne che viene Integer#succinvece chiamato (per "successore"). Ma Integer#next!(o Integer#succ!) sarebbe una sciocchezza: ricorda che i metodi funzionano sugli oggetti , non sulle variabili , quindi numb.next!sarebbero esattamente uguali a 1.next!, vale a dire, muterebbe 1 per essere uguale a 2 . ++sarebbe leggermente migliore in quanto potrebbe essere lo zucchero sintattico per un compito, ma personalmente preferisco la sintassi corrente in cui vengono eseguiti tutti i compiti =.
Philomory,

Per completare il commento sopra: e Integer#predper recuperare il predecessore.
Yoni,

-6

Controlla questi operatori della famiglia C nell'Irb di Ruby e testali tu stesso:

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

3
Questo è chiaramente sbagliato e non funziona, come (x++)è un'affermazione non valida in Ruby.
altro
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.