2009-06-04 17 views
35

E 'possibile implementare il ?? operatore in Ruby?C# ?? operatore in Ruby?

a = nil 
b = 1 

x = a ?? b # x should == 1 
x = b ?? 2 # x should == 1 

risposta

33

Siete alla ricerca di assegnazione condizionale:

a ||= b # Assign if a isn't already set 

e || operatore

a = b || 2 # Assign if b is assigned, or assign 2 
+9

Questo non funzionerà, per le ragioni che ho delineato nella mia risposta. Prova a impostare a a false nel primo esempio o b a false nel secondo esempio. –

+0

corretto, @ JörgWMittag. Attenti alla verità/falsità! –

+0

Inoltre sono abbastanza sicuro che non si possa ottenere un errore "impossibile assegnare a zero" con ??. Ma potresti scrivere un metodo ifNil su object e su NilClass per valutare un blocco come in smalltalk – Rivenfall

5
x = b || 2 

It (?? in C#) si chiama l'operatore si fondono.

+2

Questo non funzionerà, per i motivi che ho delineato nella mia risposta. Prova a impostare b su false nell'esempio. –

46

In Ruby, gli operatori booleani corto circuito (||, &&, and e or) non restituiscono true o false, ma piuttosto il primo operando che determina il risultato della intera espressione. Funziona, perché Ruby ha un'idea piuttosto semplice della verità. O meglio, ha un'idea piuttosto semplice di falsità: nil è falso, e ovviamente false è falso. Tutto il resto è vero.

Quindi, dal momento che || vale quando almeno un dei suoi operandi è vero, e gli operandi vengono valutati da sinistra a destra, ciò significa che i rendimenti a || ba, quando a è vero. Ma quando a è falso, il risultato dell'espressione dipende esclusivamente da b e pertanto viene restituito .

Ciò significa che, poiché nil è falso, è sufficiente utilizzare || anziché ?? per gli esempi forniti. (C'è anche la nifty a ||= b dell'operatore, che tipo di opere come a || a = b, ma non del tutto.)

tuttavia, che solo opere, perché non utilizzano Booleans nei tuoi esempi. Se si prevede di trattare con valori booleani, che non funziona:

b = false 

x = b || 2 # x should be == false, but will be 2 

In tal caso, si dovrà usare #nil?, e un'espressione condizionale:

b = false 

x = unless b.nil? then b else 2 end # x should be == 2 

o utilizzando il condizionale ternario operatore:

b = false 

x = b.nil? ? 2 : b # x should be == false 

Se si vuole, si può avvolgere che in un bel metodo:

class Object 
    def _? b = nil 
    return self 
    end 
end 

class NilClass 
    def _? b = nil 
    return yield if block_given? 
    return b 
    end 
end 

b = false 

x = b._? { 2 } # x should be == false 
x = b._? 2 # x should be == false 

Questo frammento carina portato a voi da polimorfismo, lezioni aperte e il fatto che nil è in realtà un oggetto che rappresenta il nulla (a differenza, ad esempio, Java, dove nullè realtà nulla).

+0

'a || = b' non è tanto" tipo di "' a || a = b', ma esattamente 'a = a || b'. Non è un compito condizionale, l'assegnazione avverrà sempre. – Theo

+5

@Theo: questo è quello che dice la bozza attuale della ISO Ruby Language Specification, ma è sbagliato. MRI, YARV, JRuby, Rubinius, XRuby, MacRuby, IronRuby, Ruby.NET, MagLev, SmallRuby, tinyrb, RubyGoLightly e ogni altra implementazione Ruby mai creata, implementarla come assegnazione condizionale di cortocircuito. The Ruby Programming Language (scritto da Matz stesso), Programming Ruby e ogni altro libro di Ruby mai scritto lo documentano in quel modo. La suite di test RubySpec la verifica in questo modo. Diverse discussioni su StackOverflow e dozzine di discussioni su ruby-talk dicono così. Lo dice lo stesso Matz. –

+0

Il link obbligatorio all'articolo di Peter Cooper: http://www.rubyinside.com/what-rubys-double-pipe-or-equals-really-does-5488.html –

1

C'è la gemma coalesce, che è il più vicino possibile.

nil || 5 # => 5 
false || 5 # => 5 :(
false._? 5 # => false :)