2009-06-04 28 views
35

¿Es posible implementar el ?? operador en Ruby?C# ?? operador en Ruby?

a = nil 
b = 1 

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

Respuesta

33

Usted está buscando asignación condicional:

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

y el || operador

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

Esto no funcionará, por las razones que expuse en mi respuesta. Intente configurar a a falso en el primer ejemplo o b a falso en el segundo ejemplo. –

+0

Correcto, @ JörgWMittag. ¡Cuidado con la verdad/falsey! –

+0

También estoy bastante seguro de que no puede obtener un error "no se puede asignar a cero" con ??. Pero podría escribir un método ifNil en object y en NilClass para evaluar un bloque como en smalltalk – Rivenfall

5
x = b || 2 

It (?? en C#) se llama el operador coalesce.

+2

Esto no funcionará, por las razones que expuse en mi respuesta. Intenta configurar b como falso en el ejemplo. –

46

En Ruby, los operadores booleanos de cortocircuito (||, &&, and y or) no regresan true o false, sino más bien el primer operando que determina el resultado de la expresión completa. Esto funciona, porque Ruby tiene una idea bastante simple de la verdad. O más bien, tiene una idea bastante simple de falsedad: nil es falso, y obviamente false es falso. Todo lo demás es cierto.

lo tanto, ya || es cierto cuando al menos un de sus operandos es verdadero, y operandos se evalúan de izquierda a derecha, esto significa que los rendimientos a || ba, cuando a es cierto. Pero cuando a es falso, entonces el resultado de la expresión depende únicamente de b, y por lo tanto b se devuelve.

Eso significa que, como nil es falso, puede simplemente usar || en lugar de para los ejemplos que proporcionó. (También existe la a ||= b operador ingenioso, qué tipo de obras como a || a = b, pero no del todo.)

Sin embargo, eso sólo obras, ya que no utilizan booleanos en sus ejemplos. Si tiene previsto tratar con valores booleanos, eso no funcionará:

b = false 

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

En ese caso, tendrá que utilizar #nil?, y una expresión condicional:

b = false 

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

o usando el condicional ternario operador:

b = false 

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

Si desea, se puede envolver en que un buen método:

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 

Este fragmento linda traído a usted por polimorfismo, clases abiertas y el hecho de que nil es en realidad un objeto que representa la nada (a diferencia, por ejemplo, Java, donde null es en realidad nada).

+0

'a || = b' no es tanto" tipo de "' a || a = b', pero exactamente 'a = a || b'. No es una tarea condicional, la tarea siempre ocurrirá. – Theo

+5

@Theo: eso es lo que dice el borrador actual de la Especificación del lenguaje ISO Ruby, pero eso está mal. MRI, YARV, JRuby, Rubinius, XRuby, MacRuby, IronRuby, Ruby.NET, MagLev, SmallRuby, tinyrb, RubyGoLightly y cualquier otra implementación de Ruby que se haya creado, implementarlo como asignación condicional de cortocircuito. El lenguaje de programación de Ruby (coescrito por el mismo matz), Programming Ruby y cualquier otro libro de Ruby escrito alguna vez lo documentan de esa manera. La suite de pruebas RubySpec lo prueba de esa manera. Varias discusiones sobre StackOverflow y docenas de discusiones sobre ruby-talk lo dicen. Matz mismo lo dice. –

+0

El enlace obligatorio al artículo de peter cooper: http://www.rubyinside.com/what-rubys-double-pipe-or-equals-really-does-5488.html –

1

Existe la gema coalesce, que es lo más cerca que obtendrá.

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