2012-06-29 18 views
7

assumendo Ho il seguente array:Rubino enumerabile inverso rilevare

views = [ 
    { :user_id => 1, :viewed_at => '2012-06-29 17:03:28 -0400' }, 
    { :user_id => 1, :viewed_at => '2012-06-29 17:04:28 -0400' }, 
    { :user_id => 2, :viewed_at => '2012-06-29 17:05:28 -0400' }, 
    { :user_id => 3, :viewed_at => '2012-06-29 17:06:28 -0400' }, 
    { :user_id => 1, :viewed_at => '2012-06-29 17:07:28 -0400' }, 
    { :user_id => 1, :viewed_at => '2012-06-29 17:08:28 -0400' }, 
    { :user_id => 3, :viewed_at => '2012-06-29 17:09:28 -0400' }, 
    { :user_id => 3, :viewed_at => '2012-06-29 17:16:28 -0400' }, 
    { :user_id => 3, :viewed_at => '2012-06-29 17:26:28 -0400' }, 
    { :user_id => 3, :viewed_at => '2012-06-29 17:36:28 -0400' }, 
    { :user_id => 1, :viewed_at => '2012-06-29 17:47:28 -0400' }, 
    { :user_id => 2, :viewed_at => '2012-06-29 17:57:28 -0400' }, 
    { :user_id => 3, :viewed_at => '2012-06-29 17:67:28 -0400' }, 
    { :user_id => 1, :viewed_at => '2012-06-29 17:77:28 -0400' } 
] 

assumendo l'array è ordinato da viewed_at

Se voglio recuperare l'ultima vista hash nei viste array per un particolare user_id, potrei fare quanto segue:

views.reverse.detect { |view| view[:user_id] == 1 } 

dove rileva restituisce il primo elemento in una enumerazione in cui il blocco restituisce true.

La mia domanda è: suppongo ci sia il costo O(n) al metodo inverso, quindi come posso rilevare al contrario senza dover invertire l'array? Oppure il metodo inverso non è O(n)?

+6

Hai davvero '17: 77' come un tempo? –

+0

Quando si concatenano i metodi, si desidera sempre concatenare gli enumeratori. La catena di enumerazione esegue una volta sola l'oggetto ed è O (n). L'esempio più comune è '" ciao ".each_char.map {| x | x.succ}' – texasbruce

+0

@texasbruce: questo sarà completamente vero con Ruby 2.0, dove saranno possibili tutti i tipi di operazioni lazy (ora molte operazioni restituiscono array, non enumeratori) – tokland

risposta

18

Il metodo Array#reverse è O (n) nel tempo e nello spazio. Dato che non è necessario l'intero array invertito, è possibile utilizzare , che sarebbe O (1) nello spazio. In pratica, ciò è rilevante solo per array molto grandi.

views.reverse_each.detect { |view| view[:user_id] == 1 } 
#=> {:user_id=>1, :viewed_at=>"2012-06-29 17:77:28 -0400"} 
+0

questo è molto interessante. Ho visto il contrario di ogni metodo nella documentazione di Ruby 1.9.3 Enumerable, ma il suo nome implica che itera su ogni oggetto in una enumerabile. Vedo dal tuo esempio che non lo fa (potenzialmente potrebbe). Ti dispiacerebbe spiegare la differenza tra tempo e spazio in questo contesto. Grazie per la tua risposta riflessiva! –

+2

"il suo nome implica che itera su ogni oggetto in un enumerabile". I metodi 'each' in Ruby sono sempre pigri, iterano l'intero enumerabile se richiesto, ma qui si fermerà quando' detect' smette di chiedere più valori. "Ti dispiacerebbe spiegare la differenza tra tempo e spazio in questo contesto". Usando reverse_each: time) è potenzialmente necessario attraversare l'intero input per trovare quello corrispondente, quindi è lineare O (n) nel peggiore dei casi, lo spazio) è sufficiente accumulare l'elemento corrente da controllare, ovvero O (1). Esempi in Python: http://wiki.python.org/moin/TimeComplexity – tokland

+0

grazie mille! –

1

Questo otterrà l'indice dall'ultimo oggetto per il quale il blocco è vero (o nullo se nessuno corrisponde).

views.rindex{|view| view[:user_id] == 1} 

Dopo @toklands di benchmarking reverse_each è (sorprendente per me) molto più veloce:

require 'benchmark' 
ar = Array.new(100){rand(100)} 
target = rand(100) 

Benchmark.bm(15) do |x| 
    x.report('reverse_each'){1000.times{ar.reverse_each.detect(target)}} 
    x.report('rindex'){1000.times{ar.rindex(target)}} 
end 


#      user  system  total  real 
#reverse_each  0.000000 0.000000 0.000000 ( 0.002981) 
#rindex   0.020000 0.000000 0.020000 ( 0.012078) 
+0

è davvero sorprendente, concettualmente è la stessa operazione, i tempi dovrebbero essere molto simili. – tokland