2012-06-29 22 views
7

zakładając Mam następującą tablicę:Ruby przeliczalny odwrotnej wykryć

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' } 
] 

zakładając tablicy jest sortowana według viewed_at

Jeśli chcę odzyskać ostatnią hash widok w poglądów tablica dla konkretny user_id, mógłbym wykonać następujące czynności:

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

gdzie wykryje, że zwróci pierwszy element w liczbie wyliczeniowej, gdzie blok jest prawdziwy.

Moje pytanie brzmi: Zakładam, że jest O(n) koszt metody odwrotnej, więc jak mogę wykryć w odwrotnej bez konieczności odwrócenia tablicy? Czy jest to metoda odwrócona, a nie ?

+6

czy naprawdę mają '17: 77' jako czas? –

+0

Po łańcuchu metod zawsze chcesz łączyć moduły wyliczające. Łańcuch enumeratorów tylko iteruje obiekt raz i jest O (n). Najczęstszym przykładem jest '" hello ".each_char.map {| x | x.succ}' – texasbruce

+0

@texasbruce: to będzie całkowicie prawdziwe z Ruby 2.0, gdzie wszystkie rodzaje leniwych operacji będą możliwe (teraz wiele operacji zwraca tablice, a nie enumeratory) – tokland

Odpowiedz

18

Metoda Array#reverse jest O (n) w czasie i przestrzeni. Ponieważ nie potrzebujesz całej odwróconej macierzy, możesz użyć Array#reverse_each, czyli O (1) w przestrzeni. W praktyce ma to znaczenie tylko w przypadku naprawdę dużych tablic.

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

jest to bardzo interesujące. Widziałem odwrotność każdej metody w bibliotece Ruby 1.9.3 Enumerable, ale jej nazwa sugeruje, że iteruje ona przez każdy obiekt w przeliczeniu. Widzę na twoim przykładzie, że tak nie jest (potencjalnie mógł). Czy mógłbyś wyjaśnić różnicę między czasem a przestrzenią w tym kontekście? Dzięki za twoją przemyślaną odpowiedź! –

+2

"jego nazwa oznacza, że ​​iteruje on przez każdy obiekt w przeliczalnym". "Każda" metoda w Ruby jest zawsze leniwą, iterują cały wyliczalny, jeśli o to poproszony, ale tutaj zatrzyma się, gdy 'detect' przestanie prosić o więcej wartości. "Czy mógłbyś wyjaśnić różnicę między czasem a przestrzenią w tym kontekście". Używając reverse_each: time) musisz potencjalnie przejść całe wejście, aby znaleźć pasujące, więc jest liniowy O (n) w najgorszym przypadku, przestrzeń) po prostu musisz zgromadzić bieżący element, który jest sprawdzany, czyli O (1). Przykłady w Pythonie: http://wiki.python.org/moin/TimeComplexity – tokland

+0

dziękuję bardzo! –

1

Spowoduje to pobranie indeksu z ostatniego obiektu, dla którego blok jest prawdziwy (lub zero, jeśli żaden nie pasuje).

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

Po benchmarkingu @toklands reverse_each jest (zaskakujące dla mnie) dużo szybciej:

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

to naprawdę zaskakujące, że koncepcyjnie to ta sama operacja, czasy powinny być bardzo podobne. – tokland

Powiązane problemy