2012-06-22 13 views
15

Ovviamente ||= non funzionaCome posso memoizzare un metodo che può restituire vero, falso o nulla in Ruby?

def x? 
    @x_query ||= expensive_way_to_calculate_x 
end 

perché se si scopre di essere false o nil, quindi expensive_way_to_calculate_x otterrà eseguire più e più volte.

Attualmente il modo migliore che conosco è quello di mettere il valore in 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 

Esiste un modo più convenzionale o efficiente di fare questo?

UPDATE ho capito che volevo Memoize nil oltre a false - questo va tutto il viaggio di ritorno a https://rails.lighthouseapp.com/projects/8994/tickets/1830-railscachefetch-does-not-work-with-false-boolean-as-cached-value - le mie scuse a Andrew Marshall che ha dato una risposta altrimenti del tutto corretta.

+0

Questo è ciò che 'nil' e l'intero concetto di' null' sono per. – meagar

+0

Ho modificato la mia domanda perché non l'avevo fatto bene la prima volta, anch'io voglio memoize nil. –

risposta

26

controllare in modo esplicito se il valore di @x_query è nil invece:

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

Si noti che se questo non era una variabile di istanza, si controlla se è stato definito anche /, invece, dal momento che tutte le variabili di istanza predefinito su nil.

dato il vostro aggiornamento che @x_query 'valore memoized s può essere nil, è possibile utilizzare defined? invece per aggirare il fatto che tutte le variabili di istanza di default per nil:

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

Si noti che fare qualcosa di simile a = 42 unless defined?(a) vinto' t funziona come previsto poiché una volta che il parser raggiunge a =, a è definito prima del raggiunge il condizionale. Tuttavia, questo non è vero con le variabili di istanza dato che per impostazione predefinita su nil il parser non le definisce quando raggiunge =. Indipendentemente da ciò, penso che sia un buon idioma utilizzare il modulo a blocco lungo or o unless anziché uno a riga unless con defined? per mantenerlo coerente.

+0

Ho intenzione di revocare questo perché è la risposta alla prima versione della mia domanda, ma per favore guarda le mie modifiche - ancora, scuse. –

+0

@SeamusAbshere Ho aggiornato la mia risposta per riflettere l'aggiornamento ':)'. –

+3

'a = 42 se non definito? (A)' rende 'a' nil, ma' @a = 42 se non definito? (@ A) 'rende' @ a' 42, quindi la sintassi 'or' non è assolutamente richiesta in questo esempio. –

18

Per rappresentano nil, utilizzare defined? per vedere se la variabile è stata definita:

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

defined? tornerà nil se non è stata definita la variabile o la stringa "instance_variable" altrimenti:

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