2012-06-22 4 views
15

Obviamente ||= no funcionará¿Cómo puedo memorizar un método que puede devolver verdadero, falso o nulo en Ruby?

def x? 
    @x_query ||= expensive_way_to_calculate_x 
end 

porque si resulta ser false o nil, entonces expensive_way_to_calculate_x obtendrá ejecutar una y otra vez.

Actualmente la mejor manera que conozco es poner el valor en un Array:

def x? 
    return @x_query.first if @x_query.is_a?(Array) 
    @x_query = [expensive_way_to_calculate_x] 
    @x_query.first 
end 

¿Hay una manera más convencional o eficiente de hacer esto?

ACTUALIZACIÓN me di cuenta de que quería memoize nil además de false - esto va todo el camino de regreso a https://rails.lighthouseapp.com/projects/8994/tickets/1830-railscachefetch-does-not-work-with-false-boolean-as-cached-value - mis disculpas a Andrew Marshall que dio una respuesta de otro modo completamente correcto.

+0

Esto es para lo que 'nil' y todo el concepto de' null' son. – meagar

+0

Modifiqué mi pregunta porque no lo había hecho bien la primera vez; también quiero memorizar nada. –

Respuesta

26

comprobar explícitamente si el valor de @x_query es nil lugar:

def x? 
    @x_query = expensive_way_to_calculate_x if @x_query.nil? 
    @x_query 
end 

Tenga en cuenta que si esto no era una variable de instancia, usted tiene que comprobar si se define también/en lugar, ya que todas las variables de instancia predeterminado a nil.

Dado su actualización que @x_query 'valor memoized s puede ser nil, puede utilizar defined? en lugar de moverse por el hecho de que todo defecto variables de instancia a nil:

def x? 
    defined?(@x_query) or @x_query = expensive_way_to_calculate_x 
    @x_query 
end 

Tenga en cuenta que hacer algo como a = 42 unless defined?(a) won' t trabajo como se esperaba ya que una vez que el analizador golpea a =, a se define antes de que llegue el condicional. Sin embargo, esto no es cierto con las variables de instancia, ya que por defecto son nil, el analizador no las define cuando toca =. A pesar de todo, creo que es una buena expresión usar el formulario de bloque largo or o unless en lugar de una línea unless con defined? para mantenerlo constante.

+0

Voy a votar esto porque es la respuesta a la primera versión de mi pregunta, pero por favor eche un vistazo a mis ediciones, nuevamente, disculpas. –

+0

@SeamusAbshere He actualizado mi respuesta para reflejar su actualización ':)'. –

+3

'a = 42 menos que se defina? (A)' 'a' hace nula, pero '@ un = 42 menos que se defina? (@ A)' '@ hace a' 42, por lo que no se requiere absolutamente el' sintaxis o ' en este ejemplo. –

18

Para tener en cuenta nil, utilice defined? para ver si la variable se ha definido:

def x? 
    return @x_query if defined? @x_query 
    @x_query = expensive_way_to_calculate_x 
end 

defined? volverá nil si no se ha definido la variable, o la cadena "instance_variable" lo contrario:

irb(main):001:0> defined? @x 
=> nil 
irb(main):002:0> @x = 3 
=> 3 
irb(main):003:0> defined? @x 
=> "instance-variable" 
Cuestiones relacionadas